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

Last change on this file was 15610, checked in by Mark Evenson, 2 years 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.