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

Last change on this file was 14025, checked in by ehuelsmann, 12 years ago

Re #60: Line missed in last commit.

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