source: branches/0.26.x/abcl/src/org/armedbear/lisp/LispThread.java

Last change on this file was 13137, checked in by ehuelsmann, 15 years ago

Unbreak trunk compilation. We need the specialNames after all.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 40.2 KB
Line 
1/*
2 * LispThread.java
3 *
4 * Copyright (C) 2003-2007 Peter Graves
5 * $Id: LispThread.java 13137 2011-01-13 20:54:59Z ehuelsmann $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import java.lang.ref.WeakReference;
37import static org.armedbear.lisp.Lisp.*;
38
39import java.util.Iterator;
40import java.util.concurrent.ConcurrentHashMap;
41import java.util.concurrent.ConcurrentLinkedQueue;
42import java.util.concurrent.atomic.AtomicInteger;
43
44public final class LispThread extends LispObject
45{
46    static boolean use_fast_calls = false;
47
48    // use a concurrent hashmap: we may want to add threads
49    // while at the same time iterating the hash
50    final static ConcurrentHashMap<Thread,LispThread> map =
51       new ConcurrentHashMap<Thread,LispThread>();
52
53    LispObject threadValue = NIL;
54
55    private static ThreadLocal<LispThread> threads = new ThreadLocal<LispThread>(){
56        @Override
57        public LispThread initialValue() {
58            Thread thisThread = Thread.currentThread();
59            LispThread thread = LispThread.map.get(thisThread);
60            if (thread == null) {
61                thread = new LispThread(thisThread);
62                LispThread.map.put(thisThread,thread);
63            }
64            return thread;
65        }
66    };
67
68    public static final LispThread currentThread()
69    {
70        return threads.get();
71    }
72
73    final Thread javaThread;
74    private boolean destroyed;
75    final LispObject name;
76    public LispObject[] _values;
77    private boolean threadInterrupted;
78    private LispObject pending = NIL;
79    private Symbol wrapper =
80        PACKAGE_THREADS.intern("THREAD-FUNCTION-WRAPPER");
81
82    LispThread(Thread javaThread)
83    {
84        this.javaThread = javaThread;
85        name = new SimpleString(javaThread.getName());
86    }
87
88    LispThread(final Function fun, LispObject name)
89    {
90        Runnable r = new Runnable() {
91            public void run()
92            {
93                try {
94                    threadValue = funcall(wrapper,
95                            new LispObject[] { fun },
96                            LispThread.this);
97                }
98                catch (ThreadDestroyed ignored) {
99                      // Might happen.
100                }
101                catch (Throwable t) { // any error: process thread interrupts
102                    if (isInterrupted()) {
103                        processThreadInterrupts();
104                    }
105                }
106                finally {
107                    // make sure the thread is *always* removed from the hash again
108                    map.remove(Thread.currentThread());
109                }
110            }
111        };
112        javaThread = new Thread(r);
113        this.name = name;
114        map.put(javaThread, this);
115        if (name != NIL)
116            javaThread.setName(name.getStringValue());
117        javaThread.setDaemon(true);
118        javaThread.start();
119    }
120
121    public StackTraceElement[] getJavaStackTrace() {
122        return javaThread.getStackTrace();
123    }
124
125    @Override
126    public LispObject typeOf()
127    {
128        return Symbol.THREAD;
129    }
130
131    @Override
132    public LispObject classOf()
133    {
134        return BuiltInClass.THREAD;
135    }
136
137    @Override
138    public LispObject typep(LispObject typeSpecifier)
139    {
140        if (typeSpecifier == Symbol.THREAD)
141            return T;
142        if (typeSpecifier == BuiltInClass.THREAD)
143            return T;
144        return super.typep(typeSpecifier);
145    }
146
147    public final synchronized boolean isDestroyed()
148    {
149        return destroyed;
150    }
151
152    final synchronized boolean isInterrupted()
153    {
154        return threadInterrupted;
155    }
156
157    final synchronized void setDestroyed(boolean b)
158    {
159        destroyed = b;
160    }
161
162    final synchronized void interrupt(LispObject function, LispObject args)
163    {
164        pending = new Cons(args, pending);
165        pending = new Cons(function, pending);
166        threadInterrupted = true;
167        javaThread.interrupt();
168    }
169
170    final synchronized void processThreadInterrupts()
171
172    {
173        while (pending != NIL) {
174            LispObject function = pending.car();
175            LispObject args = pending.cadr();
176            pending = pending.cddr();
177            Primitives.APPLY.execute(function, args);
178        }
179        threadInterrupted = false;
180    }
181
182    public final LispObject[] getValues()
183    {
184        return _values;
185    }
186
187    public final LispObject[] getValues(LispObject result, int count)
188    {
189        if (_values == null) {
190            LispObject[] values = new LispObject[count];
191            if (count > 0)
192                values[0] = result;
193            for (int i = 1; i < count; i++)
194                values[i] = NIL;
195            return values;
196        }
197        // If the caller doesn't want any extra values, just return the ones
198        // we've got.
199        if (count <= _values.length)
200            return _values;
201        // The caller wants more values than we have. Pad with NILs.
202        LispObject[] values = new LispObject[count];
203        for (int i = _values.length; i-- > 0;)
204            values[i] = _values[i];
205        for (int i = _values.length; i < count; i++)
206            values[i] = NIL;
207        return values;
208    }
209
210    // Used by the JVM compiler for MULTIPLE-VALUE-CALL.
211    public final LispObject[] accumulateValues(LispObject result,
212                                               LispObject[] oldValues)
213    {
214        if (oldValues == null) {
215            if (_values != null)
216                return _values;
217            LispObject[] values = new LispObject[1];
218            values[0] = result;
219            return values;
220        }
221        if (_values != null) {
222            if (_values.length == 0)
223                return oldValues;
224            final int totalLength = oldValues.length + _values.length;
225            LispObject[] values = new LispObject[totalLength];
226            System.arraycopy(oldValues, 0,
227                             values, 0,
228                             oldValues.length);
229            System.arraycopy(_values, 0,
230                             values, oldValues.length,
231                             _values.length);
232            return values;
233        }
234        // _values is null.
235        final int totalLength = oldValues.length + 1;
236        LispObject[] values = new LispObject[totalLength];
237        System.arraycopy(oldValues, 0,
238                         values, 0,
239                         oldValues.length);
240        values[totalLength - 1] = result;
241        return values;
242    }
243
244    public final LispObject setValues()
245    {
246        _values = new LispObject[0];
247        return NIL;
248    }
249
250    public final LispObject setValues(LispObject value1)
251    {
252        _values = null;
253        return value1;
254    }
255
256    public final LispObject setValues(LispObject value1, LispObject value2)
257    {
258        _values = new LispObject[2];
259        _values[0] = value1;
260        _values[1] = value2;
261        return value1;
262    }
263
264    public final LispObject setValues(LispObject value1, LispObject value2,
265                                      LispObject value3)
266    {
267        _values = new LispObject[3];
268        _values[0] = value1;
269        _values[1] = value2;
270        _values[2] = value3;
271        return value1;
272    }
273
274    public final LispObject setValues(LispObject value1, LispObject value2,
275                                      LispObject value3, LispObject value4)
276    {
277        _values = new LispObject[4];
278        _values[0] = value1;
279        _values[1] = value2;
280        _values[2] = value3;
281        _values[3] = value4;
282        return value1;
283    }
284
285    public final LispObject setValues(LispObject[] values)
286    {
287        switch (values.length) {
288            case 0:
289                _values = values;
290                return NIL;
291            case 1:
292                _values = null;
293                return values[0];
294            default:
295                _values = values;
296                return values[0];
297        }
298    }
299
300    public final void clearValues()
301    {
302        _values = null;
303    }
304
305    public final LispObject nothing()
306    {
307        _values = new LispObject[0];
308        return NIL;
309    }
310
311    // Forces a single value, for situations where multiple values should be
312    // ignored.
313    public final LispObject value(LispObject obj)
314    {
315        _values = null;
316        return obj;
317    }
318
319
320
321    final static int UNASSIGNED_SPECIAL_INDEX = 0;
322
323    /** Indicates the last special slot which has been assigned.
324     * Symbols which don't have a special slot assigned use a slot
325     * index of 0 for efficiency reasons: it eliminates the need to
326     * check for index validity before accessing the specials array.
327     *
328     */
329    final static AtomicInteger lastSpecial
330        = new AtomicInteger(UNASSIGNED_SPECIAL_INDEX);
331
332    /** A list of indices which can be (re)used for symbols to
333     * be assigned a special slot index.
334     */
335    final static ConcurrentLinkedQueue<Integer> freeSpecialIndices
336        = new ConcurrentLinkedQueue<Integer>();
337
338    final static int specialsInitialSize
339        = Integer.valueOf(System.getProperty("abcl.specials.initialSize","4096"));
340
341    /** This array stores the current special binding for every symbol
342     * which has been globally or locally declared special.
343     *
344     * If the array element has a null value, this means there currently
345     * is no active binding. If the array element contains a valid
346     * SpecialBinding object, but the value field of it is null, that
347     * indicates an "UNBOUND VARIABLE" situation.
348     */
349    SpecialBinding[] specials
350        = new SpecialBinding[specialsInitialSize + 1];
351
352    final static ConcurrentHashMap<Integer, WeakReference<Symbol>> specialNames
353        = new ConcurrentHashMap<Integer, WeakReference<Symbol>>();
354
355    /** The number of slots to grow the specials table in
356     * case of insufficient storage.
357     */
358    final static int specialsDelta
359        = Integer.valueOf(System.getProperty("abcl.specials.grow.delta","1024"));
360
361    /** This variable points to the head of a linked list of saved
362     * special bindings. Its main purpose is to allow a mark/reset
363     * interface to special binding and unbinding.
364     */
365    private SpecialBindingsMark savedSpecials = null;
366
367    /** Marks the state of the special bindings,
368     * for later rewinding by resetSpecialBindings().
369     */
370    public final SpecialBindingsMark markSpecialBindings() {
371        return savedSpecials;
372    }
373
374    /** Restores the state of the special bindings to what
375     * was captured in the marker 'mark' by a call to markSpecialBindings().
376     */
377    public final void resetSpecialBindings(SpecialBindingsMark mark) {
378        SpecialBindingsMark c = savedSpecials;
379        while (mark != c) {
380            specials[c.idx] = c.binding;
381            c = c.next;
382        }
383        savedSpecials = c;
384    }
385
386    /** Clears out all active special bindings including any marks
387     * previously set. Invoking resetSpecialBindings() with marks
388     * set before this call results in undefined behaviour.
389     */
390    // Package level access: only for Interpreter.run()
391    final void clearSpecialBindings() {
392        resetSpecialBindings(null);
393    }
394
395    /** Assigns a specials array index number to the symbol,
396     * if it doesn't already have one.
397     */
398    private void assignSpecialIndex(Symbol sym)
399    {
400        if (sym.specialIndex != 0)
401            return;
402
403        synchronized (sym) {
404            // Don't use an atomic access: we'll be swapping values only once.
405            if (sym.specialIndex == 0) {
406                Integer next = freeSpecialIndices.poll();
407                if (next == null
408                        && specials.length < lastSpecial.get()
409                        && null == System.getProperty("abcl.specials.grow.slowly")) {
410                    // free slots are exhausted; in the middle and at the end.
411                    System.gc();
412                    next = freeSpecialIndices.poll();
413                }
414                if (next == null)
415                    sym.specialIndex = lastSpecial.incrementAndGet();
416                else
417                    sym.specialIndex = next.intValue();
418            }
419        }
420    }
421
422    /** Frees up an index previously assigned to a symbol for re-assignment
423     * to another symbol. Returns without effect if the symbol has the
424     * default UNASSIGNED_SPECIAL_INDEX special index.
425     */
426    protected static void releaseSpecialIndex(Symbol sym)
427    {
428        int index = sym.specialIndex;
429        if (index != UNASSIGNED_SPECIAL_INDEX) {
430            // clear out the values in the
431            Iterator<LispThread> it = map.values().iterator();
432            while (it.hasNext()) {
433                LispThread thread = it.next();
434
435                // clear out the values in the saved specials list
436                SpecialBindingsMark savedSpecial = thread.savedSpecials;
437                while (savedSpecial != null) {
438                    if (savedSpecial.idx == index) {
439                        savedSpecial.idx = 0;
440                        savedSpecial.binding = null;
441                    }
442                    savedSpecial = savedSpecial.next;
443                }
444
445                thread.specials[index] = null;
446            }
447
448            freeSpecialIndices.add(new Integer(index));
449        }
450    }
451
452    private void growSpecials() {
453        SpecialBinding[] newSpecials
454                = new SpecialBinding[specials.length + specialsDelta];
455        System.arraycopy(specials, 0, newSpecials, 0, specials.length);
456        specials = newSpecials;
457    }
458
459    private SpecialBinding ensureSpecialBinding(int idx) {
460        SpecialBinding binding;
461        boolean assigned;
462        do {
463            try {
464                binding = specials[idx];
465                assigned = true;
466            }
467            catch (ArrayIndexOutOfBoundsException e) {
468                assigned = false;
469                binding = null;  // suppresses 'unassigned' error
470                growSpecials();
471            }
472        } while (! assigned);
473        return binding;
474    }
475
476    public final SpecialBinding bindSpecial(Symbol name, LispObject value)
477    {
478        int idx;
479
480        assignSpecialIndex(name);
481        SpecialBinding binding = ensureSpecialBinding(idx = name.specialIndex);
482        savedSpecials = new SpecialBindingsMark(idx, binding, savedSpecials);
483        return specials[idx] = new SpecialBinding(idx, value);
484    }
485
486    public final SpecialBinding bindSpecialToCurrentValue(Symbol name)
487    {
488        int idx;
489
490        assignSpecialIndex(name);
491        SpecialBinding binding = ensureSpecialBinding(idx = name.specialIndex);
492        savedSpecials = new SpecialBindingsMark(idx, binding, savedSpecials);
493        return specials[idx]
494            = new SpecialBinding(idx,
495                                 (binding == null) ?
496                                 name.getSymbolValue() : binding.value);
497    }
498
499    /** Looks up the value of a special binding in the context of the
500     * given thread.
501     *
502     * In order to find the value of a special variable (in general),
503     * use {@link Symbol#symbolValue}.
504     *
505     * @param name The name of the special variable, normally a symbol
506     * @return The inner most binding of the special, or null if unbound
507     *
508     * @see Symbol#symbolValue
509     */
510    public final LispObject lookupSpecial(Symbol name)
511    {
512        SpecialBinding binding = ensureSpecialBinding(name.specialIndex);
513        return (binding == null) ? null : binding.value;
514    }
515
516    public final SpecialBinding getSpecialBinding(Symbol name)
517    {
518        return ensureSpecialBinding(name.specialIndex);
519    }
520
521    public final LispObject setSpecialVariable(Symbol name, LispObject value)
522    {
523        SpecialBinding binding = ensureSpecialBinding(name.specialIndex);
524        if (binding != null)
525            return binding.value = value;
526
527        name.setSymbolValue(value);
528        return value;
529    }
530
531    public final LispObject pushSpecial(Symbol name, LispObject thing)
532
533    {
534        SpecialBinding binding = ensureSpecialBinding(name.specialIndex);
535        if (binding != null)
536            return binding.value = new Cons(thing, binding.value);
537
538        LispObject value = name.getSymbolValue();
539        if (value != null) {
540            LispObject newValue = new Cons(thing, value);
541            name.setSymbolValue(newValue);
542            return newValue;
543        } else
544            return error(new UnboundVariable(name));
545    }
546
547    // Returns symbol value or NIL if unbound.
548    public final LispObject safeSymbolValue(Symbol name)
549    {
550        SpecialBinding binding = ensureSpecialBinding(name.specialIndex);
551        if (binding != null)
552            return binding.value;
553
554        LispObject value = name.getSymbolValue();
555        return value != null ? value : NIL;
556    }
557
558    public final void rebindSpecial(Symbol name, LispObject value)
559    {
560        SpecialBinding binding = getSpecialBinding(name);
561        binding.value = value;
562    }
563
564    private LispObject catchTags = NIL;
565
566    public void pushCatchTag(LispObject tag)
567    {
568        catchTags = new Cons(tag, catchTags);
569    }
570
571    public void popCatchTag()
572    {
573        if (catchTags != NIL)
574            catchTags = catchTags.cdr();
575        else
576            Debug.assertTrue(false);
577    }
578
579    public void throwToTag(LispObject tag, LispObject result)
580
581    {
582        LispObject rest = catchTags;
583        while (rest != NIL) {
584            if (rest.car() == tag)
585                throw new Throw(tag, result, this);
586            rest = rest.cdr();
587        }
588        error(new ControlError("Attempt to throw to the nonexistent tag " +
589                                tag.writeToString() + "."));
590    }
591
592
593    private StackFrame stack = null;
594
595   public final void pushStackFrame(StackFrame frame)
596    {
597        frame.setNext(stack);
598        stack = frame;
599    }
600
601    public final void popStackFrame()
602    {
603        if (stack != null)
604            stack = stack.getNext();
605    }
606
607    public void resetStack()
608    {
609        stack = null;
610    }
611
612    @Override
613    public LispObject execute(LispObject function)
614    {
615        if (use_fast_calls)
616            return function.execute();
617
618        pushStackFrame(new LispStackFrame(function));
619        try {
620            return function.execute();
621        }
622        finally {
623            popStackFrame();
624        }
625    }
626
627    @Override
628    public LispObject execute(LispObject function, LispObject arg)
629
630    {
631        if (use_fast_calls)
632            return function.execute(arg);
633
634        pushStackFrame(new LispStackFrame(function, arg));
635        try {
636            return function.execute(arg);
637        }
638        finally {
639            popStackFrame();
640        }
641    }
642
643    @Override
644    public LispObject execute(LispObject function, LispObject first,
645                              LispObject second)
646
647    {
648        if (use_fast_calls)
649            return function.execute(first, second);
650
651        pushStackFrame(new LispStackFrame(function, first, second));
652        try {
653            return function.execute(first, second);
654        }
655        finally {
656            popStackFrame();
657        }
658    }
659
660    @Override
661    public LispObject execute(LispObject function, LispObject first,
662                              LispObject second, LispObject third)
663
664    {
665        if (use_fast_calls)
666            return function.execute(first, second, third);
667
668        pushStackFrame(new LispStackFrame(function, first, second, third));
669        try {
670            return function.execute(first, second, third);
671        }
672        finally {
673            popStackFrame();
674        }
675    }
676
677    @Override
678    public LispObject execute(LispObject function, LispObject first,
679                              LispObject second, LispObject third,
680                              LispObject fourth)
681
682    {
683        if (use_fast_calls)
684            return function.execute(first, second, third, fourth);
685
686        pushStackFrame(new LispStackFrame(function, first, second, third, fourth));
687        try {
688            return function.execute(first, second, third, fourth);
689        }
690        finally {
691            popStackFrame();
692        }
693    }
694
695    @Override
696    public LispObject execute(LispObject function, LispObject first,
697                              LispObject second, LispObject third,
698                              LispObject fourth, LispObject fifth)
699
700    {
701        if (use_fast_calls)
702            return function.execute(first, second, third, fourth, fifth);
703
704        pushStackFrame(new LispStackFrame(function, first, second, third, fourth, fifth));
705        try {
706            return function.execute(first, second, third, fourth, fifth);
707        }
708        finally {
709            popStackFrame();
710        }
711    }
712
713    @Override
714    public LispObject execute(LispObject function, LispObject first,
715                              LispObject second, LispObject third,
716                              LispObject fourth, LispObject fifth,
717                              LispObject sixth)
718
719    {
720        if (use_fast_calls)
721            return function.execute(first, second, third, fourth, fifth, sixth);
722
723        pushStackFrame(new LispStackFrame(function, first, second, 
724            third, fourth, fifth, sixth));
725        try {
726            return function.execute(first, second, third, fourth, fifth, sixth);
727        }
728        finally {
729            popStackFrame();
730        }
731    }
732
733    @Override
734    public LispObject execute(LispObject function, LispObject first,
735                              LispObject second, LispObject third,
736                              LispObject fourth, LispObject fifth,
737                              LispObject sixth, LispObject seventh)
738
739    {
740        if (use_fast_calls)
741            return function.execute(first, second, third, fourth, fifth, sixth,
742                                    seventh);
743
744        pushStackFrame(new LispStackFrame(function, first, second, third, 
745            fourth, fifth, sixth, seventh));
746        try {
747            return function.execute(first, second, third, fourth, fifth, sixth,
748                                    seventh);
749        }
750        finally {
751            popStackFrame();
752        }
753    }
754
755    public LispObject execute(LispObject function, LispObject first,
756                              LispObject second, LispObject third,
757                              LispObject fourth, LispObject fifth,
758                              LispObject sixth, LispObject seventh,
759                              LispObject eighth)
760
761    {
762        if (use_fast_calls)
763            return function.execute(first, second, third, fourth, fifth, sixth,
764                                    seventh, eighth);
765
766        pushStackFrame(new LispStackFrame(function, first, second, third, 
767            fourth, fifth, sixth, seventh, eighth));
768        try {
769            return function.execute(first, second, third, fourth, fifth, sixth,
770                                    seventh, eighth);
771        }
772        finally {
773            popStackFrame();
774        }
775    }
776
777    public LispObject execute(LispObject function, LispObject[] args)
778
779    {
780        if (use_fast_calls)
781            return function.execute(args);
782
783        pushStackFrame(new LispStackFrame(function, args));
784        try {
785            return function.execute(args);
786        }
787        finally {
788            popStackFrame();
789        }
790    }
791
792    public void printBacktrace()
793    {
794        printBacktrace(0);
795    }
796
797    public void printBacktrace(int limit)
798    {
799        if (stack != null) {
800            int count = 0;
801            Stream out =
802                checkCharacterOutputStream(Symbol.TRACE_OUTPUT.symbolValue());
803            out._writeLine("Evaluation stack:");
804            out._finishOutput();
805
806            StackFrame s = stack;
807            while (s != null) {
808                out._writeString("  ");
809                out._writeString(String.valueOf(count));
810                out._writeString(": ");
811
812                pprint(s.toLispList(), out.getCharPos(), out);
813                out.terpri();
814                out._finishOutput();
815                if (limit > 0 && ++count == limit)
816                    break;
817                s = s.next;
818            }
819        }
820    }
821
822    public LispObject backtrace(int limit)
823    {
824        LispObject result = NIL;
825        if (stack != null) {
826            int count = 0;
827            StackFrame s = stack;
828            while (s != null) {
829                result = result.push(s);
830                if (limit > 0 && ++count == limit)
831                    break;
832                s = s.getNext();
833            }
834        }
835        return result.nreverse();
836    }
837
838    public void incrementCallCounts()
839    {
840        StackFrame s = stack;
841
842        for (int i = 0; i < 8; i++) {
843            if (s == null)
844                break;
845      if (s instanceof LispStackFrame) {
846    LispObject operator = ((LispStackFrame)s).getOperator();
847    if (operator != null) {
848        operator.incrementHotCount();
849        operator.incrementCallCount();
850    }
851    s = s.getNext();
852      }
853        }
854
855        while (s != null) {
856      if (s instanceof LispStackFrame) {
857    LispObject operator = ((LispStackFrame)s).getOperator();
858    if (operator != null)
859        operator.incrementCallCount();
860      }
861      s = s.getNext();
862        }
863    }
864
865    private static void pprint(LispObject obj, int indentBy, Stream stream)
866
867    {
868        if (stream.getCharPos() == 0) {
869            StringBuffer sb = new StringBuffer();
870            for (int i = 0; i < indentBy; i++)
871                sb.append(' ');
872            stream._writeString(sb.toString());
873        }
874        String raw = obj.writeToString();
875        if (stream.getCharPos() + raw.length() < 80) {
876            // It fits.
877            stream._writeString(raw);
878            return;
879        }
880        // Object doesn't fit.
881        if (obj instanceof Cons) {
882            boolean newlineBefore = false;
883            LispObject[] array = obj.copyToArray();
884            if (array.length > 0) {
885                LispObject first = array[0];
886                if (first == Symbol.LET) {
887                    newlineBefore = true;
888                }
889            }
890            int charPos = stream.getCharPos();
891            if (newlineBefore && charPos != indentBy) {
892                stream.terpri();
893                charPos = stream.getCharPos();
894            }
895            if (charPos < indentBy) {
896                StringBuffer sb = new StringBuffer();
897                for (int i = charPos; i < indentBy; i++)
898                    sb.append(' ');
899                stream._writeString(sb.toString());
900            }
901            stream.print('(');
902            for (int i = 0; i < array.length; i++) {
903                pprint(array[i], indentBy + 2, stream);
904                if (i < array.length - 1)
905                   stream.print(' ');
906            }
907            stream.print(')');
908        } else {
909            stream.terpri();
910            StringBuffer sb = new StringBuffer();
911            for (int i = 0; i < indentBy; i++)
912                sb.append(' ');
913            stream._writeString(sb.toString());
914            stream._writeString(raw);
915            return;
916        }
917    }
918
919    @Override
920    public String writeToString()
921    {
922        StringBuffer sb = new StringBuffer("THREAD");
923        if (name != NIL) {
924            sb.append(" \"");
925            sb.append(name.getStringValue());
926            sb.append("\"");
927        }
928        return unreadableString(sb.toString());
929    }
930
931    @DocString(name="make-thread", args="function &optional &key name")
932    private static final Primitive MAKE_THREAD =
933        new Primitive("make-thread", PACKAGE_THREADS, true, "function &optional &key name")
934    {
935        @Override
936        public LispObject execute(LispObject[] args)
937        {
938            final int length = args.length;
939            if (length == 0)
940                error(new WrongNumberOfArgumentsException(this));
941            LispObject name = NIL;
942            if (length > 1) {
943                if ((length - 1) % 2 != 0)
944                    error(new ProgramError("Odd number of keyword arguments."));
945                if (length > 3)
946                    error(new WrongNumberOfArgumentsException(this));
947                if (args[1] == Keyword.NAME)
948                    name = args[2].STRING();
949                else
950                    error(new ProgramError("Unrecognized keyword argument " +
951                                            args[1].writeToString() + "."));
952            }
953            return new LispThread(checkFunction(args[0]), name);
954        }
955    };
956
957    @DocString(name="threadp", args="object",
958    doc="Boolean predicate testing if OBJECT is a thread.")
959    private static final Primitive THREADP =
960        new Primitive("threadp", PACKAGE_THREADS, true)
961    {
962        @Override
963        public LispObject execute(LispObject arg)
964        {
965            return arg instanceof LispThread ? T : NIL;
966        }
967    };
968
969    @DocString(name="thread-alive-p", args="thread",
970    doc="Returns T if THREAD is alive.")
971    private static final Primitive THREAD_ALIVE_P =
972        new Primitive("thread-alive-p", PACKAGE_THREADS, true, "thread",
973          "Boolean predicate whether THREAD is alive.")
974    {
975        @Override
976        public LispObject execute(LispObject arg)
977        {
978            final LispThread lispThread;
979            if (arg instanceof LispThread) {
980                lispThread = (LispThread) arg;
981            }
982            else {
983                return type_error(arg, Symbol.THREAD);
984            }
985            return lispThread.javaThread.isAlive() ? T : NIL;
986        }
987    };
988
989    @DocString(name="thread-name", args="thread",
990    doc="Return the name of THREAD, if it has one.")
991    private static final Primitive THREAD_NAME =
992        new Primitive("thread-name", PACKAGE_THREADS, true)
993    {
994        @Override
995        public LispObject execute(LispObject arg)
996        {
997                if (arg instanceof LispThread) {
998                return ((LispThread)arg).name;
999            }
1000                 return type_error(arg, Symbol.THREAD);
1001        }
1002    };
1003
1004    private static final Primitive THREAD_JOIN =
1005        new Primitive("thread-join", PACKAGE_THREADS, true, "thread",
1006                      "Waits for thread to finish.")
1007    {
1008        @Override
1009        public LispObject execute(LispObject arg)
1010        {
1011            // join the thread, and returns it's value.  The second return
1012            // value is T if the thread finishes normally, NIL if its
1013            // interrupted.
1014            if (arg instanceof LispThread) {               
1015                final LispThread joinedThread = (LispThread) arg;
1016                final LispThread waitingThread = currentThread();
1017                try {
1018                    joinedThread.javaThread.join();
1019                    return 
1020                        waitingThread.setValues(joinedThread.threadValue, T);
1021                } catch (InterruptedException e) {
1022                    waitingThread.processThreadInterrupts();
1023                    return 
1024                        waitingThread.setValues(joinedThread.threadValue, NIL);
1025                }
1026            } else {
1027                return type_error(arg, Symbol.THREAD);
1028            } 
1029        }
1030    };
1031
1032
1033    public static final long javaSleepInterval(LispObject lispSleep)
1034
1035    {
1036        double d =
1037            checkDoubleFloat(lispSleep.multiplyBy(new DoubleFloat(1000))).getValue();
1038        if (d < 0)
1039            type_error(lispSleep, list(Symbol.REAL, Fixnum.ZERO));
1040
1041        return (d < Long.MAX_VALUE ? (long) d : Long.MAX_VALUE);
1042    }
1043
1044    @DocString(name="sleep", args="seconds",
1045    doc="Causes the invoking thread to sleep for SECONDS seconds.\n"+
1046        "SECONDS may be a value between 0 1and 1.")
1047    private static final Primitive SLEEP = new Primitive("sleep", PACKAGE_CL, true)
1048    {
1049        @Override
1050        public LispObject execute(LispObject arg)
1051        {
1052
1053            try {
1054                Thread.sleep(javaSleepInterval(arg));
1055            }
1056            catch (InterruptedException e) {
1057                currentThread().processThreadInterrupts();
1058            }
1059            return NIL;
1060        }
1061    };
1062
1063    @DocString(name="mapcar-threads", args= "function",
1064    doc="Applies FUNCTION to all existing threads.")
1065    private static final Primitive MAPCAR_THREADS =
1066        new Primitive("mapcar-threads", PACKAGE_THREADS, true)
1067    {
1068        @Override
1069        public LispObject execute(LispObject arg)
1070        {
1071            Function fun = checkFunction(arg);
1072            final LispThread thread = LispThread.currentThread();
1073            LispObject result = NIL;
1074            Iterator it = map.values().iterator();
1075            while (it.hasNext()) {
1076                LispObject[] args = new LispObject[1];
1077                args[0] = (LispThread) it.next();
1078                result = new Cons(funcall(fun, args, thread), result);
1079            }
1080            return result;
1081        }
1082    };
1083
1084    @DocString(name="destroy-thread", args="thread", doc="Mark THREAD as destroyed")
1085    private static final Primitive DESTROY_THREAD =
1086        new Primitive("destroy-thread", PACKAGE_THREADS, true)
1087    {
1088        @Override
1089        public LispObject execute(LispObject arg)
1090        {
1091            final LispThread thread;
1092            if (arg instanceof LispThread) {
1093                thread = (LispThread) arg;
1094            }
1095            else {
1096                return type_error(arg, Symbol.THREAD);
1097            }
1098            thread.setDestroyed(true);
1099            return T;
1100        }
1101    };
1102
1103    // => T
1104    @DocString(name="interrupt-thread", args="thread function &rest args",
1105    doc="Interrupts thread and forces it to apply function to args. When the\n"+
1106        "function returns, the thread's original computation continues. If\n"+
1107        "multiple interrupts are queued for a thread, they are all run, but the\n"+
1108        "order is not guaranteed.")
1109    private static final Primitive INTERRUPT_THREAD =
1110        new Primitive("interrupt-thread", PACKAGE_THREADS, true,
1111          "thread function &rest args",
1112          "Interrupts THREAD and forces it to apply FUNCTION to ARGS.\nWhen the function returns, the thread's original computation continues. If  multiple interrupts are queued for a thread, they are all run, but the order is not guaranteed.")
1113    {
1114        @Override
1115        public LispObject execute(LispObject[] args)
1116        {
1117            if (args.length < 2)
1118                return error(new WrongNumberOfArgumentsException(this));
1119            final LispThread thread;
1120            if (args[0] instanceof LispThread) {
1121                thread = (LispThread) args[0];
1122            }
1123            else {
1124                return type_error(args[0], Symbol.THREAD);
1125            }
1126            LispObject fun = args[1];
1127            LispObject funArgs = NIL;
1128            for (int i = args.length; i-- > 2;)
1129                funArgs = new Cons(args[i], funArgs);
1130            thread.interrupt(fun, funArgs);
1131            return T;
1132        }
1133    };
1134
1135    @DocString(name="current-thread",
1136    doc="Returns a reference to invoking thread.")
1137    private static final Primitive CURRENT_THREAD =
1138        new Primitive("current-thread", PACKAGE_THREADS, true)
1139    {
1140        @Override
1141        public LispObject execute()
1142        {
1143            return currentThread();
1144        }
1145    };
1146
1147    @DocString(name="backtrace",
1148               doc="Returns a backtrace of the invoking thread.")
1149    private static final Primitive BACKTRACE =
1150        new Primitive("backtrace", PACKAGE_SYS, true)
1151    {
1152        @Override
1153        public LispObject execute(LispObject[] args)
1154
1155        {
1156            if (args.length > 1)
1157                return error(new WrongNumberOfArgumentsException(this));
1158            int limit = args.length > 0 ? Fixnum.getValue(args[0]) : 0;
1159            return currentThread().backtrace(limit);
1160        }
1161    };
1162    @DocString(name="frame-to-string", args="frame")
1163    private static final Primitive FRAME_TO_STRING =
1164        new Primitive("frame-to-string", PACKAGE_SYS, true)
1165    {
1166        @Override
1167        public LispObject execute(LispObject[] args)
1168
1169        {
1170            if (args.length != 1)
1171                return error(new WrongNumberOfArgumentsException(this));
1172           
1173            return checkStackFrame(args[0]).toLispString();
1174        }
1175    };
1176
1177    @DocString(name="frame-to-list", args="frame")
1178    private static final Primitive FRAME_TO_LIST =
1179        new Primitive("frame-to-list", PACKAGE_SYS, true)
1180    {
1181        @Override
1182        public LispObject execute(LispObject[] args)
1183
1184        {
1185            if (args.length != 1)
1186                return error(new WrongNumberOfArgumentsException(this));
1187
1188            return checkStackFrame(args[0]).toLispList();
1189        }
1190    };
1191
1192
1193    @DocString(name="use-fast-calls")
1194    private static final Primitive USE_FAST_CALLS =
1195        new Primitive("use-fast-calls", PACKAGE_SYS, true)
1196    {
1197        @Override
1198        public LispObject execute(LispObject arg)
1199        {
1200            use_fast_calls = (arg != NIL);
1201            return use_fast_calls ? T : NIL;
1202        }
1203    };
1204
1205    @DocString(name="synchronized-on", args="form &body body")
1206    private static final SpecialOperator SYNCHRONIZED_ON =
1207        new SpecialOperator("synchronized-on", PACKAGE_THREADS, true,
1208                            "form &body body")
1209    {
1210        @Override
1211        public LispObject execute(LispObject args, Environment env)
1212
1213        {
1214          if (args == NIL)
1215            return error(new WrongNumberOfArgumentsException(this));
1216
1217          LispThread thread = LispThread.currentThread();
1218          synchronized (eval(args.car(), env, thread).lockableInstance()) {
1219              return progn(args.cdr(), env, thread);
1220          }
1221        }
1222    };
1223
1224    @DocString(name="object-wait", args="object &optional timeout")
1225    private static final Primitive OBJECT_WAIT =
1226        new Primitive("object-wait", PACKAGE_THREADS, true)
1227    {
1228        @Override
1229        public LispObject execute(LispObject object)
1230
1231        {
1232            try {
1233                object.lockableInstance().wait();
1234            }
1235            catch (InterruptedException e) {
1236                currentThread().processThreadInterrupts();
1237            }
1238            catch (IllegalMonitorStateException e) {
1239                return error(new IllegalMonitorState());
1240            }
1241            return NIL;
1242        }
1243
1244        @Override
1245        public LispObject execute(LispObject object, LispObject timeout)
1246
1247        {
1248            try {
1249                object.lockableInstance().wait(javaSleepInterval(timeout));
1250            }
1251            catch (InterruptedException e) {
1252                currentThread().processThreadInterrupts();
1253            }
1254            catch (IllegalMonitorStateException e) {
1255                return error(new IllegalMonitorState());
1256            }
1257            return NIL;
1258        }
1259    };
1260
1261    @DocString(name="object-notify", args="object")
1262    private static final Primitive OBJECT_NOTIFY =
1263        new Primitive("object-notify", PACKAGE_THREADS, true,
1264                      "object")
1265    {
1266        @Override
1267        public LispObject execute(LispObject object)
1268
1269        {
1270            try {
1271                object.lockableInstance().notify();
1272            }
1273            catch (IllegalMonitorStateException e) {
1274                return error(new IllegalMonitorState());
1275            }
1276            return NIL;
1277        }
1278    };
1279
1280    @DocString(name="object-notify-all", args="object")
1281    private static final Primitive OBJECT_NOTIFY_ALL =
1282        new Primitive("object-notify-all", PACKAGE_THREADS, true)
1283    {
1284        @Override
1285        public LispObject execute(LispObject object)
1286
1287        {
1288            try {
1289                object.lockableInstance().notifyAll();
1290            }
1291            catch (IllegalMonitorStateException e) {
1292                return error(new IllegalMonitorState());
1293            }
1294            return NIL;
1295        }
1296    };
1297
1298
1299}
Note: See TracBrowser for help on using the repository browser.