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

Last change on this file was 13540, checked in by Mark Evenson, 14 years ago

Pass wrapped error message to IllegalMonitorException?.

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