source: trunk/abcl/src/org/armedbear/lisp/LispThread.java

Last change on this file was 15610, checked in by Mark Evenson, 17 months ago

Fix virtual threading interfaces

Native threads are now the default, even if the underlying platform
affords us a virtual threads interface.

The ability to invoke virtual threads is indicated by the presence of
the :VIRTUAL-THREADS in CL:*FEATURES*

Virtual threads may be spawned by setting the value of
THREADS:*THREADING-MODEL* to :VIRTUAL before invoking
THREADS:MAKE-THREAD.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 56.6 KB
Line 
1/*
2 * LispThread.java
3 *
4 * Copyright (C) 2003-2007 Peter Graves
5 * $Id: LispThread.java 15610 2022-12-03 02:44:22Z 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 java.lang.reflect.Method;
38import java.lang.reflect.InvocationTargetException;
39import static org.armedbear.lisp.Lisp.*;
40
41import java.util.Iterator;
42import java.util.concurrent.ConcurrentHashMap;
43import java.util.concurrent.ConcurrentLinkedQueue;
44import java.util.concurrent.atomic.AtomicInteger;
45import java.util.Stack; 
46import java.text.MessageFormat;
47
48import java.util.concurrent.ThreadFactory;
49
50public final class LispThread
51  extends LispObject
52  implements org.armedbear.lisp.protocol.Inspectable
53{
54    // use a concurrent hashmap: we may want to add threads
55    // while at the same time iterating the hash
56    final static ConcurrentHashMap<Thread,LispThread> map =
57       new ConcurrentHashMap<Thread,LispThread>();
58
59    LispObject threadValue = NIL;
60
61    private static ThreadLocal<LispThread> threads = new ThreadLocal<LispThread>(){
62        @Override
63        public LispThread initialValue() {
64            Thread thisThread = Thread.currentThread();
65            LispThread thread = LispThread.map.get(thisThread);
66            if (thread == null) {
67                thread = new LispThread(thisThread);
68                LispThread.map.put(thisThread,thread);
69            }
70            return thread;
71        }
72    };
73
74    public static final LispThread currentThread()
75    {
76        return threads.get();
77    }
78
79    final Thread javaThread;
80    private boolean destroyed;
81    final LispObject name;
82    public LispObject[] _values;
83    private boolean threadInterrupted;
84    private LispObject pending = NIL;
85    private Symbol wrapper =
86        PACKAGE_THREADS.intern("THREAD-FUNCTION-WRAPPER");
87
88    /** Whether the wrapped thread is virtual */
89    boolean isVirtual;
90
91    public LispObject getParts() {
92      return NIL
93        .push(new Cons("Wrapped Java thread",
94                       JavaObject.getInstance(javaThread)));
95    }
96
97    /** Stack holding bindings for evaluated functions */
98    public Stack<Environment>envStack = new Stack<Environment>();
99
100    LispThread(Thread javaThread)
101    {
102        this.javaThread = javaThread;
103        name = new SimpleString(javaThread.getName());
104    }
105
106  public static boolean virtualThreadingAvailable() {
107    try {
108      Method m
109        = java.lang.Thread.class
110        .getDeclaredMethod("startVirtualThread", java.lang.Runnable.class);
111      return true;
112    } catch (NoSuchMethodException e2) {
113      // This is the case in non-Loom JVMs
114    } catch (SecurityException e3) {
115      Debug.trace("SecurityException caught introspecting threading interface: " + e3.toString());
116    }
117    return false;
118  }
119
120  static ThreadFactory virtualThreadFactory;
121  static Method newThread;
122
123  static {
124    try {
125      Object ofVirtual
126        = Thread.class.getMethod("ofVirtual").invoke(null);
127      Method factoryMethod
128        = Class.forName("java.lang.Thread$Builder").getMethod("factory");
129      virtualThreadFactory
130        = (ThreadFactory) factoryMethod.invoke(ofVirtual);
131      newThread 
132        = virtualThreadFactory.getClass()
133        .getMethod("newThread", java.lang.Runnable.class);
134      newThread.setAccessible(true);
135    } catch (Exception e) {
136      if (virtualThreadingAvailable()) {
137        Debug.trace("Failed to introspect virtual threading methods: " + e);
138      }
139    }
140  }
141
142  public static Symbol NATIVE_THREADS = internKeyword("NATIVE");
143  public static Symbol VIRTUAL_THREADS = internKeyword("VIRTUAL");
144
145  static {
146    Symbol._THREADING_MODEL_.initializeSpecial(NATIVE_THREADS);
147    final String summary
148      = "The current type of threads created via MAKE-THREAD";
149    final String doc
150      = "Meaningful values are either :NATIVE, the default, or the keyword :VIRTUAL."
151      + " The ability to invoke virtual threads at runtime is indicated by the presence of :VIRTUAL-THREADS"
152      + " in CL:*FEATURES*.";
153    Symbol._THREADING_MODEL_.setDocumentation(new SimpleString(summary + "\n" + doc),
154                                              Symbol.VARIABLE);
155  }
156
157    LispThread(final Function fun, LispObject name)
158    {
159        Runnable r = new Runnable() {
160            public void run()
161            {
162                try {
163                    threadValue = funcall(wrapper,
164                            new LispObject[] { fun },
165                            LispThread.this);
166                }
167                catch (ThreadDestroyed ignored) {
168                      // Might happen.
169                }
170                catch (ProcessingTerminated e) {
171                    System.exit(e.getStatus());
172                }
173                catch (Throwable t) { // any error: process thread interrupts
174                    if (isInterrupted()) {
175                        processThreadInterrupts();
176                    }
177                    String msg
178                        = MessageFormat.format("Ignoring uncaught exception {0}.",
179                                               t.toString());
180                    Debug.warn(msg);
181                }
182                finally {
183                    // make sure the thread is *always* removed from the hash again
184                    map.remove(Thread.currentThread());
185                }
186            }
187        };
188        this.name = name;
189
190        Thread thread = null;
191       
192        if (Symbol._THREADING_MODEL_.getSymbolValue().equals(NATIVE_THREADS)) {
193          thread = new Thread(r);
194          isVirtual = false;
195        } else {
196          try {
197            thread = (Thread)newThread.invoke(virtualThreadFactory, r);
198            isVirtual = true;
199          } catch (Exception e) {
200            thread = new Thread(r);
201            isVirtual =  false;
202            Debug.trace("Falling back to native thread creation after virtual thread failed:", e);
203          }
204        }
205        if (thread == null) {
206          Debug.trace("Failed to create java.lang.Thread");
207          javaThread = null;
208        } else {
209          if (name != NIL) {
210            thread.setName(name.getStringValue());
211          }
212          thread.setDaemon(true);
213
214          javaThread = thread;
215          map.put(javaThread, this);
216
217          javaThread.start();
218        }
219    }
220
221    public StackTraceElement[] getJavaStackTrace() {
222        return javaThread.getStackTrace();
223    }
224
225    @Override
226    public LispObject typeOf()
227    {
228        return Symbol.THREAD;
229    }
230
231    @Override
232    public LispObject classOf()
233    {
234        return BuiltInClass.THREAD;
235    }
236
237    @Override
238    public LispObject typep(LispObject typeSpecifier)
239    {
240        if (typeSpecifier == Symbol.THREAD)
241            return T;
242        if (typeSpecifier == BuiltInClass.THREAD)
243            return T;
244        return super.typep(typeSpecifier);
245    }
246
247    public final synchronized boolean isDestroyed()
248    {
249        return destroyed;
250    }
251
252    final synchronized boolean isInterrupted()
253    {
254        return threadInterrupted;
255    }
256
257    final synchronized void setDestroyed(boolean b)
258    {
259        destroyed = b;
260    }
261
262    final synchronized void interrupt(LispObject function, LispObject args)
263    {
264        pending = new Cons(args, pending);
265        pending = new Cons(function, pending);
266        threadInterrupted = true;
267        javaThread.interrupt();
268    }
269
270    final synchronized void processThreadInterrupts()
271
272    {
273        while (pending != NIL) {
274            LispObject function = pending.car();
275            LispObject args = pending.cadr();
276            pending = pending.cddr();
277            Primitives.APPLY.execute(function, args);
278        }
279        threadInterrupted = false;
280    }
281
282    public final LispObject[] getValues()
283    {
284        return _values;
285    }
286
287    public final LispObject[] getValues(LispObject result, int count)
288    {
289        if (_values == null) {
290            LispObject[] values = new LispObject[count];
291            if (count > 0)
292                values[0] = result;
293            for (int i = 1; i < count; i++)
294                values[i] = NIL;
295            return values;
296        }
297        // If the caller doesn't want any extra values, just return the ones
298        // we've got.
299        if (count <= _values.length)
300            return _values;
301        // The caller wants more values than we have. Pad with NILs.
302        LispObject[] values = new LispObject[count];
303        for (int i = _values.length; i-- > 0;)
304            values[i] = _values[i];
305        for (int i = _values.length; i < count; i++)
306            values[i] = NIL;
307        return values;
308    }
309
310    /** Used by the JVM compiler for MULTIPLE-VALUE-CALL. */
311    public final LispObject[] accumulateValues(LispObject result,
312                                               LispObject[] oldValues)
313    {
314        if (oldValues == null) {
315            if (_values != null)
316                return _values;
317            LispObject[] values = new LispObject[1];
318            values[0] = result;
319            return values;
320        }
321        if (_values != null) {
322            if (_values.length == 0)
323                return oldValues;
324            final int totalLength = oldValues.length + _values.length;
325            LispObject[] values = new LispObject[totalLength];
326            System.arraycopy(oldValues, 0,
327                             values, 0,
328                             oldValues.length);
329            System.arraycopy(_values, 0,
330                             values, oldValues.length,
331                             _values.length);
332            return values;
333        }
334        // _values is null.
335        final int totalLength = oldValues.length + 1;
336        LispObject[] values = new LispObject[totalLength];
337        System.arraycopy(oldValues, 0,
338                         values, 0,
339                         oldValues.length);
340        values[totalLength - 1] = result;
341        return values;
342    }
343
344    public final LispObject setValues()
345    {
346        _values = new LispObject[0];
347        return NIL;
348    }
349
350    public final LispObject setValues(LispObject value1)
351    {
352        _values = null;
353        return value1;
354    }
355
356    public final LispObject setValues(LispObject value1, LispObject value2)
357    {
358        _values = new LispObject[2];
359        _values[0] = value1;
360        _values[1] = value2;
361        return value1;
362    }
363
364    public final LispObject setValues(LispObject value1, LispObject value2,
365                                      LispObject value3)
366    {
367        _values = new LispObject[3];
368        _values[0] = value1;
369        _values[1] = value2;
370        _values[2] = value3;
371        return value1;
372    }
373
374    public final LispObject setValues(LispObject value1, LispObject value2,
375                                      LispObject value3, LispObject value4)
376    {
377        _values = new LispObject[4];
378        _values[0] = value1;
379        _values[1] = value2;
380        _values[2] = value3;
381        _values[3] = value4;
382        return value1;
383    }
384
385    public final LispObject setValues(LispObject[] values)
386    {
387        switch (values.length) {
388            case 0:
389                _values = values;
390                return NIL;
391            case 1:
392                _values = null;
393                return values[0];
394            default:
395                _values = values;
396                return values[0];
397        }
398    }
399
400    public final void clearValues()
401    {
402        _values = null;
403    }
404
405    public final LispObject nothing()
406    {
407        _values = new LispObject[0];
408        return NIL;
409    }
410
411   /**
412    * Force a single value, for situations where multiple values should be
413    * ignored.
414    */
415    public final LispObject value(LispObject obj)
416    {
417        _values = null;
418        return obj;
419    }
420
421
422
423    final static int UNASSIGNED_SPECIAL_INDEX = 0;
424
425    /** Indicates the last special slot which has been assigned.
426     * Symbols which don't have a special slot assigned use a slot
427     * index of 0 for efficiency reasons: it eliminates the need to
428     * check for index validity before accessing the specials array.
429     *
430     */
431    final static AtomicInteger lastSpecial
432        = new AtomicInteger(UNASSIGNED_SPECIAL_INDEX);
433
434    /** A list of indices which can be (re)used for symbols to
435     * be assigned a special slot index.
436     */
437    final static ConcurrentLinkedQueue<Integer> freeSpecialIndices
438        = new ConcurrentLinkedQueue<Integer>();
439
440    final static int specialsInitialSize
441        = Integer.valueOf(System.getProperty("abcl.specials.initialSize","4096"));
442
443    /** This array stores the current special binding for every symbol
444     * which has been globally or locally declared special.
445     *
446     * If the array element has a null value, this means there currently
447     * is no active binding. If the array element contains a valid
448     * SpecialBinding object, but the value field of it is null, that
449     * indicates an "UNBOUND VARIABLE" situation.
450     */
451    SpecialBinding[] specials
452        = new SpecialBinding[specialsInitialSize + 1];
453
454    final static ConcurrentHashMap<Integer, WeakReference<Symbol>> specialNames
455        = new ConcurrentHashMap<Integer, WeakReference<Symbol>>();
456
457    /** The number of slots to grow the specials table in
458     * case of insufficient storage.
459     */
460    final static int specialsDelta
461        = Integer.valueOf(System.getProperty("abcl.specials.grow.delta","1024"));
462
463    /** This variable points to the head of a linked list of saved
464     * special bindings. Its main purpose is to allow a mark/reset
465     * interface to special binding and unbinding.
466     */
467    private SpecialBindingsMark savedSpecials = null;
468
469    /** Marks the state of the special bindings,
470     * for later rewinding by resetSpecialBindings().
471     */
472    public final SpecialBindingsMark markSpecialBindings() {
473        return savedSpecials;
474    }
475
476    /** Restores the state of the special bindings to what
477     * was captured in the marker 'mark' by a call to markSpecialBindings().
478     */
479    public final void resetSpecialBindings(SpecialBindingsMark mark) {
480        SpecialBindingsMark c = savedSpecials;
481        while (mark != c) {
482            specials[c.idx] = c.binding;
483            c = c.next;
484        }
485        savedSpecials = c;
486    }
487
488    /** Clears out all active special bindings including any marks
489     * previously set. Invoking resetSpecialBindings() with marks
490     * set before this call results in undefined behaviour.
491     */
492    // Package level access: only for Interpreter.run()
493    final void clearSpecialBindings() {
494        resetSpecialBindings(null);
495    }
496
497    /** Assigns a specials array index number to the symbol,
498     * if it doesn't already have one.
499     */
500    private void assignSpecialIndex(Symbol sym)
501    {
502        if (sym.specialIndex != 0)
503            return;
504
505        synchronized (sym) {
506            // Don't use an atomic access: we'll be swapping values only once.
507            if (sym.specialIndex == 0) {
508                Integer next = freeSpecialIndices.poll();
509                if (next == null
510                        && specials.length < lastSpecial.get()
511                        && null == System.getProperty("abcl.specials.grow.slowly")) {
512                    // free slots are exhausted; in the middle and at the end.
513                    System.gc();
514                    next = freeSpecialIndices.poll();
515                }
516                if (next == null)
517                    sym.specialIndex = lastSpecial.incrementAndGet();
518                else
519                    sym.specialIndex = next.intValue();
520            }
521        }
522    }
523
524    /** Frees up an index previously assigned to a symbol for re-assignment
525     * to another symbol. Returns without effect if the symbol has the
526     * default UNASSIGNED_SPECIAL_INDEX special index.
527     */
528    protected static void releaseSpecialIndex(Symbol sym)
529    {
530        int index = sym.specialIndex;
531        if (index != UNASSIGNED_SPECIAL_INDEX) {
532            // clear out the values in the
533            Iterator<LispThread> it = map.values().iterator();
534            while (it.hasNext()) {
535                LispThread thread = it.next();
536
537                // clear out the values in the saved specials list
538                SpecialBindingsMark savedSpecial = thread.savedSpecials;
539                while (savedSpecial != null) {
540                    if (savedSpecial.idx == index) {
541                        savedSpecial.idx = 0;
542                        savedSpecial.binding = null;
543                    }
544                    savedSpecial = savedSpecial.next;
545                }
546
547                thread.specials[index] = null;
548            }
549
550            freeSpecialIndices.add(Integer.valueOf(index));
551        }
552    }
553
554    private void growSpecials() {
555        SpecialBinding[] newSpecials
556                = new SpecialBinding[specials.length + specialsDelta];
557        System.arraycopy(specials, 0, newSpecials, 0, specials.length);
558        specials = newSpecials;
559    }
560
561    private SpecialBinding ensureSpecialBinding(int idx) {
562        SpecialBinding binding;
563        boolean assigned;
564        do {
565            try {
566                binding = specials[idx];
567                assigned = true;
568            }
569            catch (ArrayIndexOutOfBoundsException e) {
570                assigned = false;
571                binding = null;  // suppresses 'unassigned' error
572                growSpecials();
573            }
574        } while (! assigned);
575        return binding;
576    }
577
578    public final SpecialBinding bindSpecial(Symbol name, LispObject value)
579    {
580        int idx;
581
582        assignSpecialIndex(name);
583        SpecialBinding binding = ensureSpecialBinding(idx = name.specialIndex);
584        savedSpecials = new SpecialBindingsMark(idx, binding, savedSpecials);
585        return specials[idx] = new SpecialBinding(idx, value);
586    }
587
588    public final SpecialBinding bindSpecialToCurrentValue(Symbol name)
589    {
590        int idx;
591
592        assignSpecialIndex(name);
593        SpecialBinding binding = ensureSpecialBinding(idx = name.specialIndex);
594        savedSpecials = new SpecialBindingsMark(idx, binding, savedSpecials);
595        return specials[idx]
596            = new SpecialBinding(idx,
597                                 (binding == null) ?
598                                 name.getSymbolValue() : binding.value);
599    }
600
601    /** Looks up the value of a special binding in the context of the
602     * given thread.
603     *
604     * In order to find the value of a special variable (in general),
605     * use {@link Symbol#symbolValue}.
606     *
607     * @param name The name of the special variable, normally a symbol
608     * @return The inner most binding of the special, or null if unbound
609     *
610     * @see Symbol#symbolValue
611     */
612    public final LispObject lookupSpecial(Symbol name)
613    {
614        SpecialBinding binding = ensureSpecialBinding(name.specialIndex);
615        return (binding == null) ? null : binding.value;
616    }
617
618    public final SpecialBinding getSpecialBinding(Symbol name)
619    {
620        return ensureSpecialBinding(name.specialIndex);
621    }
622
623    public final LispObject setSpecialVariable(Symbol name, LispObject value)
624    {
625        SpecialBinding binding = ensureSpecialBinding(name.specialIndex);
626        if (binding != null)
627            return binding.value = value;
628
629        name.setSymbolValue(value);
630        return value;
631    }
632
633    public final LispObject pushSpecial(Symbol name, LispObject thing)
634
635    {
636        SpecialBinding binding = ensureSpecialBinding(name.specialIndex);
637        if (binding != null)
638            return binding.value = new Cons(thing, binding.value);
639
640        LispObject value = name.getSymbolValue();
641        if (value != null) {
642            LispObject newValue = new Cons(thing, value);
643            name.setSymbolValue(newValue);
644            return newValue;
645        } else
646            return error(new UnboundVariable(name));
647    }
648
649    // Returns symbol value or NIL if unbound.
650    public final LispObject safeSymbolValue(Symbol name)
651    {
652        SpecialBinding binding = ensureSpecialBinding(name.specialIndex);
653        if (binding != null)
654            return binding.value;
655
656        LispObject value = name.getSymbolValue();
657        return value != null ? value : NIL;
658    }
659
660    public final void rebindSpecial(Symbol name, LispObject value)
661    {
662        SpecialBinding binding = getSpecialBinding(name);
663        binding.value = value;
664    }
665
666    private LispObject catchTags = NIL;
667
668    public void pushCatchTag(LispObject tag)
669    {
670        catchTags = new Cons(tag, catchTags);
671    }
672
673    public void popCatchTag()
674    {
675        if (catchTags != NIL)
676            catchTags = catchTags.cdr();
677        else
678            Debug.assertTrue(false);
679    }
680
681    public void throwToTag(LispObject tag, LispObject result)
682
683    {
684        LispObject rest = catchTags;
685        while (rest != NIL) {
686            if (rest.car() == tag)
687                throw new Throw(tag, result, this);
688            rest = rest.cdr();
689        }
690        error(new ControlError("Attempt to throw to the nonexistent tag " +
691                                tag.princToString() + "."));
692    }
693
694
695    private static class StackMarker {
696
697        final int numArgs;
698
699        StackMarker(int numArgs) {
700            this.numArgs = numArgs;
701        }
702
703        int getNumArgs() {
704            return numArgs;
705        }
706    }
707
708    // markers for args
709    private final static StackMarker STACK_MARKER_0 = new StackMarker(0);
710    private final static StackMarker STACK_MARKER_1 = new StackMarker(1);
711    private final static StackMarker STACK_MARKER_2 = new StackMarker(2);
712    private final static StackMarker STACK_MARKER_3 = new StackMarker(3);
713    private final static StackMarker STACK_MARKER_4 = new StackMarker(4);
714    private final static StackMarker STACK_MARKER_5 = new StackMarker(5);
715    private final static StackMarker STACK_MARKER_6 = new StackMarker(6);
716    private final static StackMarker STACK_MARKER_7 = new StackMarker(7);
717    private final static StackMarker STACK_MARKER_8 = new StackMarker(8);
718
719    private final int STACK_FRAME_EXTRA = 2;
720    // a LispStackFrame with n arguments occupies n + STACK_FRAME_EXTRA elements
721    // in {@code stack} array.
722    // stack[framePos] == operation
723    // stack[framePos + 1 + i] == arg[i]
724    // stack[framePos + 1 + n] == initially SrackMarker(n)
725    // LispStackFrame object may be lazily allocated later.
726    // In this case it is stored in stack framePos + 1 + n]
727    //
728    // Java stack frame occupies 1 element
729    // stack[framePos] == JavaStackFrame
730    //
731    // Stack consists of a list of StackSegments.
732    // Top StackSegment is cached in variables stack and stackPtr.
733    private StackSegment topStackSegment = new StackSegment(INITIAL_SEGMENT_SIZE, null);
734    private Object[] stack = topStackSegment.stack;
735    private int stackPtr = 0;
736    private StackSegment spareStackSegment;
737   
738    private static class StackSegment 
739      implements org.armedbear.lisp.protocol.Inspectable
740    {
741        final Object[] stack;
742        final StackSegment next;
743        int stackPtr;
744       
745        StackSegment(int size, StackSegment next) {
746            stack = new Object[size];
747            this.next = next;
748        }
749        public LispObject getParts() {
750        return NIL
751          .push(new Cons(new Symbol("INITIAL-SEGMENT-SIZE"),
752                         LispInteger.getInstance(LispThread.INITIAL_SEGMENT_SIZE)))
753          .push(new Cons(new Symbol("SEGMENT-SIZE"),
754                         LispInteger.getInstance(LispThread.SEGMENT_SIZE)));
755        }
756    }
757   
758    private void ensureStackCapacity(int itemsToPush) {
759        if (stackPtr + (itemsToPush - 1) >= stack.length)
760            grow(itemsToPush);
761    }
762
763    private static final int INITIAL_SEGMENT_SIZE = 1 << 10;
764    private static final int SEGMENT_SIZE = (1 << 19) - 4; // 4 MiB page on x86_64
765
766    private void grow(int numEntries) {
767        topStackSegment.stackPtr = stackPtr;
768        if (spareStackSegment != null) {
769            // Use spare segement if available
770            if (stackPtr > 0 && spareStackSegment.stack.length >= numEntries) {
771                topStackSegment = spareStackSegment;
772                stack = topStackSegment.stack;
773                spareStackSegment = null;
774                stackPtr = 0;
775                return;
776            }
777            spareStackSegment = null;
778        }
779        int newSize = stackPtr + numEntries;
780        if (topStackSegment.stack.length < SEGMENT_SIZE || stackPtr == 0) {
781            // grow initial segment from initial size to standard size
782            int newLength = Math.max(newSize, Math.min(SEGMENT_SIZE, stack.length * 2));
783            StackSegment newSegment = new StackSegment(newLength, topStackSegment.next);
784            System.arraycopy(stack, 0, newSegment.stack, 0, stackPtr);
785            topStackSegment = newSegment;
786            stack = topStackSegment.stack;
787            return;
788        }
789        // Allocate new segment
790        topStackSegment = new StackSegment(Math.max(SEGMENT_SIZE, numEntries), topStackSegment);
791        stack = topStackSegment.stack;
792        stackPtr = 0;
793    }
794
795    private StackFrame getStackTop() {
796        topStackSegment.stackPtr = stackPtr;
797        if (stackPtr == 0) {
798            assert topStackSegment.next == null;
799            return null;
800        }
801        StackFrame prev = null;
802        for (StackSegment segment = topStackSegment; segment != null; segment = segment.next) {
803            Object[] stk = segment.stack;
804            int framePos = segment.stackPtr;
805            while (framePos > 0) {
806                Object stackObj = stk[framePos - 1];
807                if (stackObj instanceof StackFrame) {
808                    if (prev != null) {
809                        prev.setNext((StackFrame) stackObj);
810                    }
811                    return (StackFrame) stack[stackPtr - 1];
812                }
813                StackMarker marker = (StackMarker) stackObj;
814                int numArgs = marker.getNumArgs();
815                LispStackFrame frame = new LispStackFrame(stk, framePos - numArgs - STACK_FRAME_EXTRA, numArgs);
816                frame.thread = this;
817                stk[framePos - 1] = frame;
818                if (prev != null) {
819                    prev.setNext(frame);
820                }
821                prev = frame;
822                framePos -= numArgs + STACK_FRAME_EXTRA;
823            }
824        }
825        return (StackFrame) stack[stackPtr - 1];
826    }
827   
828    public final void pushStackFrame(JavaStackFrame frame) {
829        frame.setNext(getStackTop());
830        ensureStackCapacity(1);
831        stack[stackPtr] = frame;
832        stackPtr += 1;
833    }
834
835    private void popStackFrame(int numArgs) {
836        // Pop off intervening JavaFrames until we get back to a LispFrame
837        Object stackObj = stack[stackPtr - 1];
838        if (stackObj instanceof StackMarker) {
839            assert numArgs == ((StackMarker) stackObj).getNumArgs();
840        } else {
841            while (stackObj instanceof JavaStackFrame) {
842                stack[--stackPtr] = null;
843                stackObj = stack[stackPtr - 1];
844            }
845            if (stackObj instanceof StackMarker) {
846                assert numArgs == ((StackMarker) stackObj).getNumArgs();
847            } else {
848                assert numArgs == ((LispStackFrame) stackObj).getNumArgs();
849            }
850        }
851        stackPtr -= numArgs + STACK_FRAME_EXTRA;
852        for (int i = 0; i < numArgs + STACK_FRAME_EXTRA; i++) {
853            stack[stackPtr + i] = null;
854        }
855        if (stackPtr == 0) {
856            popStackSegment();
857        }
858    }
859   
860    private void popStackSegment() {
861        topStackSegment.stackPtr = 0;
862        if (topStackSegment.next != null) {
863            spareStackSegment = topStackSegment;
864            topStackSegment = topStackSegment.next;
865            stack = topStackSegment.stack;
866        }
867        stackPtr = topStackSegment.stackPtr;
868    }
869
870    public final Environment setEnv(Environment env) {
871        StackFrame stackTop = getStackTop();
872        return (stackTop != null) ? stackTop.setEnv(env) : null;
873    }
874
875    public void resetStack()
876    {
877        topStackSegment = new StackSegment(INITIAL_SEGMENT_SIZE, null);
878        stack = topStackSegment.stack;
879        spareStackSegment = null;
880        stackPtr = 0;
881    }
882
883    @Override
884    public LispObject execute(LispObject function)
885    {
886        ensureStackCapacity(STACK_FRAME_EXTRA);
887        stack[stackPtr] = function;
888        stack[stackPtr + 1] = STACK_MARKER_0;
889        stackPtr += STACK_FRAME_EXTRA;
890        try {
891            envStack.push(new Environment(null,NIL,function));
892            return function.execute();
893        }
894        finally {
895            envStack.pop() ; 
896            popStackFrame(0);
897        }
898    }
899
900    @Override
901    public LispObject execute(LispObject function, LispObject arg)
902    {
903        ensureStackCapacity(1 + STACK_FRAME_EXTRA);
904        stack[stackPtr] = function;
905        stack[stackPtr + 1] = arg;
906        stack[stackPtr + 2] = STACK_MARKER_1;
907        stackPtr += 1 + STACK_FRAME_EXTRA;
908        try {
909            envStack.push(new Environment(null,NIL,function));
910            return function.execute(arg);
911        }
912        finally {
913            envStack.pop() ; 
914            popStackFrame(1);
915        }
916    }
917
918    @Override
919    public LispObject execute(LispObject function, LispObject first,
920                              LispObject second)
921    {
922        ensureStackCapacity(2 + STACK_FRAME_EXTRA);
923        stack[stackPtr] = function;
924        stack[stackPtr + 1] = first;
925        stack[stackPtr + 2] = second;
926        stack[stackPtr + 3] = STACK_MARKER_2;
927        stackPtr += 2 + STACK_FRAME_EXTRA;
928        try {
929            envStack.push(new Environment(null,NIL,function));
930            return function.execute(first, second);
931        }
932        finally {
933            envStack.pop() ; 
934            popStackFrame(2);
935        }
936    }
937
938    @Override
939    public LispObject execute(LispObject function, LispObject first,
940                              LispObject second, LispObject third)
941    {
942        ensureStackCapacity(3 + STACK_FRAME_EXTRA);
943        stack[stackPtr] = function;
944        stack[stackPtr + 1] = first;
945        stack[stackPtr + 2] = second;
946        stack[stackPtr + 3] = third;
947        stack[stackPtr + 4] = STACK_MARKER_3;
948        stackPtr += 3 + STACK_FRAME_EXTRA;
949        try {
950            envStack.push(new Environment(null,NIL,function));
951            return function.execute(first, second, third);
952        }
953        finally {
954            envStack.pop() ; 
955            popStackFrame(3);
956        }
957    }
958
959    @Override
960    public LispObject execute(LispObject function, LispObject first,
961                              LispObject second, LispObject third,
962                              LispObject fourth)
963    {
964        ensureStackCapacity(4 + STACK_FRAME_EXTRA);
965        stack[stackPtr] = function;
966        stack[stackPtr + 1] = first;
967        stack[stackPtr + 2] = second;
968        stack[stackPtr + 3] = third;
969        stack[stackPtr + 4] = fourth;
970        stack[stackPtr + 5] = STACK_MARKER_4;
971        stackPtr += 4 + STACK_FRAME_EXTRA;
972        try {
973            envStack.push(new Environment(null,NIL,function));
974            return function.execute(first, second, third, fourth);
975        }
976        finally {
977            envStack.pop() ; 
978            popStackFrame(4);
979        }
980    }
981
982    @Override
983    public LispObject execute(LispObject function, LispObject first,
984                              LispObject second, LispObject third,
985                              LispObject fourth, LispObject fifth)
986    {
987        ensureStackCapacity(5 + STACK_FRAME_EXTRA);
988        stack[stackPtr] = function;
989        stack[stackPtr + 1] = first;
990        stack[stackPtr + 2] = second;
991        stack[stackPtr + 3] = third;
992        stack[stackPtr + 4] = fourth;
993        stack[stackPtr + 5] = fifth;
994        stack[stackPtr + 6] = STACK_MARKER_5;
995        stackPtr += 5 + STACK_FRAME_EXTRA;
996        try {
997            envStack.push(new Environment(null,NIL,function));
998            return function.execute(first, second, third, fourth, fifth);
999        }
1000        finally {
1001            envStack.pop() ; 
1002            popStackFrame(5);
1003        }
1004    }
1005
1006    @Override
1007    public LispObject execute(LispObject function, LispObject first,
1008                              LispObject second, LispObject third,
1009                              LispObject fourth, LispObject fifth,
1010                              LispObject sixth)
1011    {
1012        ensureStackCapacity(6 + STACK_FRAME_EXTRA);
1013        stack[stackPtr] = function;
1014        stack[stackPtr + 1] = first;
1015        stack[stackPtr + 2] = second;
1016        stack[stackPtr + 3] = third;
1017        stack[stackPtr + 4] = fourth;
1018        stack[stackPtr + 5] = fifth;
1019        stack[stackPtr + 6] = sixth;
1020        stack[stackPtr + 7] = STACK_MARKER_6;
1021        stackPtr += 6 + STACK_FRAME_EXTRA;
1022        try {
1023            envStack.push(new Environment(null,NIL,function));
1024            return function.execute(first, second, third, fourth, fifth, sixth);
1025        }
1026        finally {
1027            envStack.pop() ; 
1028            popStackFrame(6);
1029        }
1030    }
1031
1032    @Override
1033    public LispObject execute(LispObject function, LispObject first,
1034                              LispObject second, LispObject third,
1035                              LispObject fourth, LispObject fifth,
1036                              LispObject sixth, LispObject seventh)
1037    {
1038        ensureStackCapacity(7 + STACK_FRAME_EXTRA);
1039        stack[stackPtr] = function;
1040        stack[stackPtr + 1] = first;
1041        stack[stackPtr + 2] = second;
1042        stack[stackPtr + 3] = third;
1043        stack[stackPtr + 4] = fourth;
1044        stack[stackPtr + 5] = fifth;
1045        stack[stackPtr + 6] = sixth;
1046        stack[stackPtr + 7] = seventh;
1047        stack[stackPtr + 8] = STACK_MARKER_7;
1048        stackPtr += 7 + STACK_FRAME_EXTRA;
1049        try {
1050            envStack.push(new Environment(null,NIL,function));
1051            return function.execute(first, second, third, fourth, fifth, sixth,
1052                                    seventh);
1053        }
1054        finally {
1055            envStack.pop();
1056            popStackFrame(7);
1057        }
1058    }
1059
1060    public LispObject execute(LispObject function, LispObject first,
1061                              LispObject second, LispObject third,
1062                              LispObject fourth, LispObject fifth,
1063                              LispObject sixth, LispObject seventh,
1064                              LispObject eighth)
1065    {
1066        ensureStackCapacity(8 + STACK_FRAME_EXTRA);
1067        stack[stackPtr] = function;
1068        stack[stackPtr + 1] = first;
1069        stack[stackPtr + 2] = second;
1070        stack[stackPtr + 3] = third;
1071        stack[stackPtr + 4] = fourth;
1072        stack[stackPtr + 5] = fifth;
1073        stack[stackPtr + 6] = sixth;
1074        stack[stackPtr + 7] = seventh;
1075        stack[stackPtr + 8] = eighth;
1076        stack[stackPtr + 9] = STACK_MARKER_8;
1077        stackPtr += 8 + STACK_FRAME_EXTRA;
1078        try {
1079            envStack.push(new Environment(null,NIL,function));
1080            return function.execute(first, second, third, fourth, fifth, sixth,
1081                                    seventh, eighth);
1082        }
1083        finally {
1084            envStack.pop() ; 
1085            popStackFrame(8);
1086        }
1087    }
1088
1089    public LispObject execute(LispObject function, LispObject[] args)
1090    {
1091        ensureStackCapacity(args.length + STACK_FRAME_EXTRA);
1092        stack[stackPtr] = function;
1093        System.arraycopy(args, 0, stack, stackPtr + 1, args.length);
1094        stack[stackPtr + args.length + 1] = new StackMarker(args.length);
1095        stackPtr += args.length + STACK_FRAME_EXTRA;
1096        try {
1097            envStack.push(new Environment(null,NIL,function));
1098            return function.execute(args);
1099        }
1100        finally {
1101            envStack.pop() ; 
1102            popStackFrame(args.length);
1103        }
1104    }
1105
1106    public void printBacktrace()
1107    {
1108        printBacktrace(0);
1109    }
1110
1111    public void printBacktrace(int limit)
1112    {
1113        StackFrame stackTop = getStackTop();
1114        if (stackTop != null) {
1115            int count = 0;
1116            Stream out =
1117                checkCharacterOutputStream(Symbol.TRACE_OUTPUT.symbolValue());
1118            out._writeLine("Evaluation stack:");
1119            out._finishOutput();
1120
1121            StackFrame s = stackTop;
1122            while (s != null) {
1123                out._writeString("  ");
1124                out._writeString(String.valueOf(count));
1125                out._writeString(": ");
1126
1127                pprint(s.toLispList(), out.getCharPos(), out);
1128                out.terpri();
1129                out._finishOutput();
1130                if (limit > 0 && ++count == limit)
1131                    break;
1132                s = s.next;
1133            }
1134        }
1135    }
1136
1137    public LispObject backtrace(int limit)
1138    {
1139        StackFrame stackTop = getStackTop();
1140        LispObject result = NIL;
1141        if (stackTop != null) {
1142            int count = 0;
1143            StackFrame s = stackTop;
1144            while (s != null) {
1145                result = result.push(s);
1146                if (limit > 0 && ++count == limit)
1147                    break;
1148                s = s.getNext();
1149            }
1150        }
1151        return result.nreverse();
1152    }
1153
1154    public void incrementCallCounts()
1155    {
1156        topStackSegment.stackPtr = stackPtr;
1157        int depth = 0;
1158        for (StackSegment segment = topStackSegment; segment != null; segment = segment.next) {
1159            Object[] stk = segment.stack;
1160            int framePos = segment.stackPtr;
1161            while (framePos > 0) {
1162                depth++;
1163                Object stackObj = stk[framePos - 1];
1164                int numArgs;
1165                if (stackObj instanceof StackMarker) {
1166                    numArgs = ((StackMarker) stackObj).getNumArgs();
1167                } else if (stackObj instanceof LispStackFrame) {
1168                    numArgs = ((LispStackFrame) stackObj).getNumArgs();
1169                } else {
1170                    assert stackObj instanceof JavaStackFrame;
1171                    framePos--;
1172                    continue;
1173                }
1174                // lisp stack frame
1175                framePos -= numArgs + STACK_FRAME_EXTRA;
1176                LispObject operator = (LispObject) stack[framePos];
1177                if (operator != null) {
1178                    if (depth <= 8) {
1179                        operator.incrementHotCount();
1180                    }
1181                    operator.incrementCallCount();
1182                }
1183            }
1184        }
1185    }
1186
1187    private static void pprint(LispObject obj, int indentBy, Stream stream)
1188
1189    {
1190        if (stream.getCharPos() == 0) {
1191            StringBuffer sb = new StringBuffer();
1192            for (int i = 0; i < indentBy; i++)
1193                sb.append(' ');
1194            stream._writeString(sb.toString());
1195        }
1196        String raw = obj.printObject();
1197        if (stream.getCharPos() + raw.length() < 80) {
1198            // It fits.
1199            stream._writeString(raw);
1200            return;
1201        }
1202        // Object doesn't fit.
1203        if (obj instanceof Cons) {
1204            boolean newlineBefore = false;
1205            LispObject[] array = obj.copyToArray();
1206            if (array.length > 0) {
1207                LispObject first = array[0];
1208                if (first == Symbol.LET) {
1209                    newlineBefore = true;
1210                }
1211            }
1212            int charPos = stream.getCharPos();
1213            if (newlineBefore && charPos != indentBy) {
1214                stream.terpri();
1215                charPos = stream.getCharPos();
1216            }
1217            if (charPos < indentBy) {
1218                StringBuffer sb = new StringBuffer();
1219                for (int i = charPos; i < indentBy; i++)
1220                    sb.append(' ');
1221                stream._writeString(sb.toString());
1222            }
1223            stream.print('(');
1224            for (int i = 0; i < array.length; i++) {
1225                pprint(array[i], indentBy + 2, stream);
1226                if (i < array.length - 1)
1227                   stream.print(' ');
1228            }
1229            stream.print(')');
1230        } else {
1231            stream.terpri();
1232            StringBuffer sb = new StringBuffer();
1233            for (int i = 0; i < indentBy; i++)
1234                sb.append(' ');
1235            stream._writeString(sb.toString());
1236            stream._writeString(raw);
1237            return;
1238        }
1239    }
1240
1241    @Override
1242    public String printObject()
1243    {
1244        StringBuffer sb = new StringBuffer("THREAD");
1245        if (name != NIL) {
1246            sb.append(" \"");
1247            sb.append(name.getStringValue());
1248            sb.append("\"");
1249        }
1250        if (isVirtual) {
1251          sb.append(" virtual");
1252        } else {
1253          sb.append(" native");
1254        }
1255        return unreadableString(sb.toString());
1256    }
1257
1258    @DocString(name="make-thread",
1259               args="function &key name",
1260               doc="Create a thread of execution running FUNCTION possibly named NAME")
1261    private static final Primitive MAKE_THREAD =
1262        new Primitive("make-thread", PACKAGE_THREADS, true, "function &key name")
1263    {
1264        @Override
1265        public LispObject execute(LispObject[] args)
1266        {
1267            final int length = args.length;
1268            if (length == 0)
1269                error(new WrongNumberOfArgumentsException(this, 1, -1));
1270            LispObject name = NIL;
1271            if (length > 1) {
1272                if ((length - 1) % 2 != 0)
1273                    program_error("Odd number of keyword arguments.");
1274                if (length > 3)
1275                    error(new WrongNumberOfArgumentsException(this, -1, 2)); // don't count the keyword itself as an argument
1276                if (args[1] == Keyword.NAME)
1277                    name = args[2].STRING();
1278                else
1279                    program_error("Unrecognized keyword argument "
1280                                  + args[1].princToString() + ".");
1281            }
1282            return new LispThread(checkFunction(args[0]), name);
1283        }
1284    };
1285
1286    @DocString(name="threadp",
1287               args="object",
1288               doc="Boolean predicate returning non-nil if OBJECT is a lisp thread")
1289    private static final Primitive THREADP =
1290        new Primitive("threadp", PACKAGE_THREADS, true)
1291    {
1292        @Override
1293        public LispObject execute(LispObject arg)
1294        {
1295            return arg instanceof LispThread ? T : NIL;
1296        }
1297    };
1298
1299    @DocString(name="thread-alive-p",
1300               args="thread",
1301               doc="Returns T if THREAD is alive.")
1302    private static final Primitive THREAD_ALIVE_P =
1303      new Primitive("thread-alive-p", PACKAGE_THREADS, true, "thread",
1304                    "Boolean predicate whether THREAD is alive.")
1305    {
1306        @Override
1307        public LispObject execute(LispObject arg)
1308        {
1309            final LispThread lispThread;
1310            if (arg instanceof LispThread) {
1311                lispThread = (LispThread) arg;
1312            }
1313            else {
1314                return type_error(arg, Symbol.THREAD);
1315            }
1316            return lispThread.javaThread.isAlive() ? T : NIL;
1317        }
1318    };
1319
1320    @DocString(name="thread-name",
1321               args="thread",
1322               doc="Return the name of THREAD, if it has one.")
1323    private static final Primitive THREAD_NAME =
1324        new Primitive("thread-name", PACKAGE_THREADS, true)
1325    {
1326        @Override
1327        public LispObject execute(LispObject arg)
1328        {
1329                if (arg instanceof LispThread) {
1330                return ((LispThread)arg).name;
1331            }
1332                 return type_error(arg, Symbol.THREAD);
1333        }
1334    };
1335
1336    private static final Primitive THREAD_JOIN =
1337      new Primitive("thread-join", PACKAGE_THREADS, true, "thread",
1338                    "Waits for THREAD to die before resuming execution\n"
1339                    + "Returns the result of the joined thread as its primary value.\n"
1340                    + "Returns T if the joined thread finishes normally or NIL if it was interrupted.")
1341    {
1342        @Override
1343        public LispObject execute(LispObject arg)
1344        {
1345            // join the thread, and returns its value.  The second return
1346            // value is T if the thread finishes normally, NIL if its
1347            // interrupted.
1348            if (arg instanceof LispThread) {               
1349                final LispThread joinedThread = (LispThread) arg;
1350                final LispThread waitingThread = currentThread();
1351                try {
1352                    joinedThread.javaThread.join();
1353                    return 
1354                        waitingThread.setValues(joinedThread.threadValue, T);
1355                } catch (InterruptedException e) {
1356                    waitingThread.processThreadInterrupts();
1357                    return 
1358                        waitingThread.setValues(joinedThread.threadValue, NIL);
1359                }
1360            } else {
1361                return type_error(arg, Symbol.THREAD);
1362            } 
1363        }
1364    };
1365   
1366    final static DoubleFloat THOUSAND = new DoubleFloat(1000);
1367
1368    static final long sleepMillisPart(LispObject seconds) {
1369      double d
1370        = checkDoubleFloat(seconds.multiplyBy(THOUSAND)).getValue();
1371      if (d < 0) {
1372        type_error(seconds, list(Symbol.REAL, Fixnum.ZERO));
1373      }
1374      return (d < Long.MAX_VALUE ? (long) d : Long.MAX_VALUE);
1375    }
1376
1377    static final int sleepNanosPart(LispObject seconds) {
1378      double d  // d contains millis
1379        = checkDoubleFloat(seconds.multiplyBy(THOUSAND)).getValue();
1380      double n = d * 1000000; // sleep interval in nanoseconds
1381      d = 1.0e6 * ((long)d); //  sleep interval to millisecond precision
1382      n = n - d; 
1383
1384      return (n < Integer.MAX_VALUE ? (int) n : Integer.MAX_VALUE);
1385    }
1386
1387
1388    @DocString(name="sleep", args="seconds",
1389    doc="Causes the invoking thread to sleep for an interveral expressed in SECONDS.\n"
1390      + "SECONDS may be specified as a fraction of a second, with intervals\n"
1391      + "less than or equal to a nanosecond resulting in a yield of execution\n"
1392      + "to other waiting threads rather than an actual sleep.\n"
1393      + "A zero value of SECONDS *may* result in the JVM sleeping indefinitely,\n"
1394      + "depending on the implementation.")
1395    private static final Primitive SLEEP = new Primitive("sleep", PACKAGE_CL, true)
1396    {
1397        @Override
1398        public LispObject execute(LispObject arg)
1399        {
1400          long millis = sleepMillisPart(arg);
1401          int nanos = sleepNanosPart(arg);
1402          boolean zeroArgP = arg.ZEROP() != NIL;
1403
1404          try {
1405            if (millis == 0 && nanos == 0) { 
1406              if (zeroArgP) {
1407                Thread.sleep(0, 0);
1408              } else { 
1409                Thread.sleep(0, 1);
1410              }
1411            } else {
1412              Thread.sleep(millis, nanos);
1413            } 
1414          } catch (InterruptedException e) {
1415            currentThread().processThreadInterrupts();
1416          }
1417          return NIL;
1418        }
1419    };
1420
1421    @DocString(name="mapcar-threads", args= "function",
1422    doc="Applies FUNCTION to all existing threads.")
1423    private static final Primitive MAPCAR_THREADS =
1424        new Primitive("mapcar-threads", PACKAGE_THREADS, true)
1425    {
1426        @Override
1427        public LispObject execute(LispObject arg)
1428        {
1429            Function fun = checkFunction(arg);
1430            final LispThread thread = LispThread.currentThread();
1431            LispObject result = NIL;
1432            Iterator it = map.values().iterator();
1433            while (it.hasNext()) {
1434                LispObject[] args = new LispObject[1];
1435                args[0] = (LispThread) it.next();
1436                result = new Cons(funcall(fun, args, thread), result);
1437            }
1438            return result;
1439        }
1440    };
1441
1442    @DocString(name="destroy-thread", args="thread", doc="Mark THREAD as destroyed")
1443    private static final Primitive DESTROY_THREAD =
1444        new Primitive("destroy-thread", PACKAGE_THREADS, true)
1445    {
1446        @Override
1447        public LispObject execute(LispObject arg)
1448        {
1449            final LispThread thread;
1450            if (arg instanceof LispThread) {
1451                thread = (LispThread) arg;
1452            }
1453            else {
1454                return type_error(arg, Symbol.THREAD);
1455            }
1456            thread.setDestroyed(true);
1457            return T;
1458        }
1459    };
1460
1461    // => T
1462    @DocString(name="interrupt-thread", args="thread function &rest args",
1463    doc="Interrupts thread and forces it to apply function to args. When the\n"+
1464        "function returns, the thread's original computation continues. If\n"+
1465        "multiple interrupts are queued for a thread, they are all run, but the\n"+
1466        "order is not guaranteed.")
1467    private static final Primitive INTERRUPT_THREAD =
1468        new Primitive("interrupt-thread", PACKAGE_THREADS, true,
1469              "thread function &rest args",
1470              "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.")
1471    {
1472        @Override
1473        public LispObject execute(LispObject[] args)
1474        {
1475            if (args.length < 2)
1476                return error(new WrongNumberOfArgumentsException(this, 2, -1));
1477            final LispThread thread;
1478            if (args[0] instanceof LispThread) {
1479                thread = (LispThread) args[0];
1480            }
1481            else {
1482                return type_error(args[0], Symbol.THREAD);
1483            }
1484            LispObject fun = args[1];
1485            LispObject funArgs = NIL;
1486            for (int i = args.length; i-- > 2;)
1487                funArgs = new Cons(args[i], funArgs);
1488            thread.interrupt(fun, funArgs);
1489            setInterrupted(thread,true);
1490            return T;
1491        }
1492    };
1493
1494    public static final Primitive CURRENT_THREAD
1495      = new pf_current_thread();
1496    @DocString(name="current-thread",
1497               doc="Returns a reference to invoking thread.")
1498    private static final class pf_current_thread extends Primitive {
1499      pf_current_thread() {
1500        super("current-thread", PACKAGE_THREADS, true);
1501      }
1502      @Override
1503      public LispObject execute() {
1504        return currentThread();
1505      }
1506    };
1507
1508    public static final Primitive BACKTRACE
1509      = new pf_backtrace();
1510    @DocString(name="backtrace",
1511               doc="Returns a Java backtrace of the invoking thread.")
1512    private static final class pf_backtrace extends Primitive {
1513      pf_backtrace() {
1514        super("backtrace", PACKAGE_SYS, true);
1515      }
1516      @Override
1517      public LispObject execute(LispObject[] args) {
1518        if (args.length > 1)
1519          return error(new WrongNumberOfArgumentsException(this, -1, 1));
1520        int limit = args.length > 0 ? Fixnum.getValue(args[0]) : 0;
1521        return currentThread().backtrace(limit);
1522      }
1523    };
1524
1525    public static final Primitive FRAME_TO_STRING
1526      = new pf_frame_to_string();
1527    @DocString(name="frame-to-string", 
1528               args="frame",
1529               doc="Convert stack FRAME to a (potentially) readable string.")
1530    private static final class pf_frame_to_string extends Primitive {
1531      pf_frame_to_string() {
1532        super("frame-to-string", PACKAGE_SYS, true);
1533      }
1534      @Override
1535      public LispObject execute(LispObject[] args) {
1536        if (args.length != 1)
1537          return error(new WrongNumberOfArgumentsException(this, 1));
1538        return checkStackFrame(args[0]).toLispString();
1539      }
1540    };
1541
1542    public static final Primitive FRAME_TO_LIST
1543      = new pf_frame_to_list();
1544    @DocString(name="frame-to-list", args="frame")
1545    private static final class pf_frame_to_list extends Primitive {
1546      pf_frame_to_list() {
1547        super("frame-to-list", PACKAGE_SYS, true);
1548      }
1549      @Override
1550      public LispObject execute(LispObject[] args) {
1551        if (args.length != 1)
1552          return error(new WrongNumberOfArgumentsException(this, 1));
1553
1554        return checkStackFrame(args[0]).toLispList();
1555      }
1556    };
1557
1558
1559    public static final SpecialOperator SYNCHRONIZED_ON
1560      = new so_synchronized_on();
1561    @DocString(name="synchronized-on", args="form &body body")
1562    private static final class so_synchronized_on extends SpecialOperator {
1563      so_synchronized_on() {
1564        super("synchronized-on", PACKAGE_THREADS, true, "form &body body");
1565      }
1566      @Override
1567      public LispObject execute(LispObject args, Environment env) {
1568        if (args == NIL)
1569          return error(new WrongNumberOfArgumentsException(this, 1));
1570       
1571        LispThread thread = LispThread.currentThread();
1572        synchronized (eval(args.car(), env, thread).lockableInstance()) {
1573          return progn(args.cdr(), env, thread);
1574        }
1575      }
1576    };
1577
1578 
1579    public static final Primitive OBJECT_WAIT
1580      = new pf_object_wait();
1581    @DocString(
1582    name="object-wait", args="object &optional timeout", 
1583    doc="Causes the current thread to block until object-notify or object-notify-all is called on OBJECT.\n"
1584       + "Optionally unblock execution after TIMEOUT seconds.  A TIMEOUT of zero\n"
1585       + "means to wait indefinitely.\n"
1586       + "A non-zero TIMEOUT of less than a nanosecond is interpolated as a nanosecond wait."
1587       + "\n"
1588       + "See the documentation of java.lang.Object.wait() for further\n"
1589       + "information.\n"
1590    )
1591    private static final class pf_object_wait extends Primitive {
1592      pf_object_wait() {
1593        super("object-wait", PACKAGE_THREADS, true);
1594      }
1595      @Override
1596      public LispObject execute(LispObject object) {
1597        try {
1598          object.lockableInstance().wait();
1599        } catch (InterruptedException e) {
1600          currentThread().processThreadInterrupts();
1601        } catch (IllegalMonitorStateException e) {
1602          return error(new IllegalMonitorState(e.getMessage()));
1603        }
1604        return NIL;
1605      }
1606
1607      @Override
1608      public LispObject execute(LispObject object, LispObject timeout) {
1609        long millis = sleepMillisPart(timeout);
1610        int nanos = sleepNanosPart(timeout);
1611        boolean zeroArgP = timeout.ZEROP() != NIL;
1612         
1613        try {
1614          if (millis == 0 && nanos == 0) { 
1615            if (zeroArgP) {
1616              object.lockableInstance().wait(0, 0);
1617            } else {
1618              object.lockableInstance().wait(0, 1);
1619            }
1620          } else {
1621            object.lockableInstance().wait(millis, nanos);
1622          }
1623        } catch (InterruptedException e) {
1624          currentThread().processThreadInterrupts();
1625        } catch (IllegalMonitorStateException e) {
1626          return error(new IllegalMonitorState(e.getMessage()));
1627        }
1628        return NIL;
1629      }
1630    };
1631
1632    public static final Primitive OBJECT_NOTIFY
1633      = new pf_object_notify();
1634    @DocString(name="object-notify", 
1635               args="object",
1636               doc="Wakes up a single thread that is waiting on OBJECT's monitor."
1637+ "\nIf any threads are waiting on this object, one of them is chosen to be"
1638+ " awakened. The choice is arbitrary and occurs at the discretion of the"
1639+ " implementation. A thread waits on an object's monitor by calling one"
1640+ " of the wait methods.")
1641    private static final class pf_object_notify extends Primitive {
1642      pf_object_notify() {
1643        super("object-notify", PACKAGE_THREADS, true, "object");
1644      }
1645      @Override
1646      public LispObject execute(LispObject object) {
1647        try {
1648          object.lockableInstance().notify();
1649        } catch (IllegalMonitorStateException e) {
1650          return error(new IllegalMonitorState(e.getMessage()));
1651        }
1652        return NIL;
1653      }
1654    };
1655
1656    public static final Primitive OBJECT_NOTIFY_ALL
1657      = new pf_object_notify_all();
1658    @DocString(name="object-notify-all", 
1659               args="object",
1660               doc="Wakes up all threads that are waiting on this OBJECT's monitor."
1661+ "\nA thread waits on an object's monitor by calling one of the wait methods.")
1662    private static final class pf_object_notify_all extends Primitive {
1663      pf_object_notify_all() {
1664        super("object-notify-all", PACKAGE_THREADS, true);
1665      }
1666      @Override
1667      public LispObject execute(LispObject object) {
1668        try {
1669          object.lockableInstance().notifyAll();
1670        } catch (IllegalMonitorStateException e) {
1671          return error(new IllegalMonitorState(e.getMessage()));
1672        }
1673        return NIL;
1674      }
1675    };
1676}
Note: See TracBrowser for help on using the repository browser.