Thread.avt

Переключить прокрутку окна
Загрузить этот исходный код

/*
    Реализация среды исполнения языка программирования
    Объектно-ориентированный продвинутый векторный транслятор

    Copyright © 2021, 2024, 2026 Малик Разработчик

    Это свободная программа: вы можете перераспространять ее и/или изменять
    ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она будет полезной,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <https://www.gnu.org/licenses/>.
*/

package avt.lang;

import platform.dependent.*;

public class Thread(Object, Runnable)
{
    public static final int MIN_PRIORITY  = 1;
    public static final int LOW_PRIORITY  = 3;
    public static final int NORM_PRIORITY = 5;
    public static final int HIGH_PRIORITY = 7;
    public static final int MAX_PRIORITY  = 9;

    private static final int TAG_SF_METHOD = 0x5f;

    private static int activeLength;
    private static ThreadEntry[] activeTable;
    private static Mutex activeOperation;
    private static UnhandledExceptionHandler systemUnhandledExceptionHandler;
    private static UnhandledExceptionHandler defaultUnhandledExceptionHandler;

    public static void yield() { PlatformServices.getInstance().yieldThread(); }

    public static void sleep(long timeInMillis) throws InterruptedException {
        if(timeInMillis < 0)
        {
            throw new IllegalArgumentException(String.format(package.getResourceString("illegal-argument.negative"), new Object[] { "timeInMillis" }));
        }
        if(timeInMillis == 0)
        {
            PlatformServices.getInstance().yieldThread();
            return;
        }
        Thread current = Thread.current();
        current.fldEvent.wait(timeInMillis, current.interruptioHandler);
    }

    public static void sleep(long timeInMillis, int timeInNanos) throws InterruptedException {
        if(timeInMillis < 0)
        {
            throw new IllegalArgumentException(String.format(package.getResourceString("illegal-argument.negative"), new Object[] { "timeInMillis" }));
        }
        if(timeInNanos < 0 || timeInNanos > 999999)
        {
            throw new IllegalArgumentException(String.format(package.getResourceString("illegal-argument.out-of-range"), new Object[] { "timeInNanos" }));
        }
        if(timeInMillis == 0 && timeInNanos == 0)
        {
            PlatformServices.getInstance().yieldThread();
            return;
        }
        Thread current = Thread.current();
        current.fldEvent.wait(timeInMillis, timeInNanos, current.interruptioHandler);
    }

    public static void setDefaultUnhandledExceptionHandler(UnhandledExceptionHandler handler) { defaultUnhandledExceptionHandler = handler == null ? systemUnhandledExceptionHandler : handler; }

    public static boolean holdsLock(Object instance) {
        if(instance == null)
        {
            throw new NullPointerException(String.format(package.getResourceString("null-pointer.argument"), new Object[] { "instance" }));
        }
        Mutex monitor = instance.monitor;
        return monitor != null && monitor.isHold();
    }

    public static boolean interrupted() { return Thread.current().isInterrupted(); }

    public static int activeCount() { return PlatformServices.getInstance().getActiveThreadsQuantity(); }

	/* В документации метода enumerate обязательно указать, что он — только для мониторинга и отладки! */
    public static Thread[] enumerate() {
        long[] threadsIds = PlatformServices.getInstance().getActiveThreadsMainIds();
        int length = threadsIds.length;
        Thread[] result = new Thread[length];
        Mutex operation = activeOperation;
        operation.lock();
        try
        {
            while(length-- > 0) result[length] = query(threadsIds[length]);
        } finally
        {
            operation.unlock();
        }
        return result;
    }

    public static StackTraceElement[] stackTrace() {
        long stackPtr = getCallerStackPointer();
        if(stackPtr == 0) return new StackTraceElement[0];
        int length = 0;
        StackTraceElement[] result = new StackTraceElement[0x0f];
        try
        {
            long dataPtr = getDataPointer();
            long tagsPtr = getTagsPointer();
            int stackLength = getStackLength();
            int stackIndex = (int) (stackPtr - dataPtr >> 4);
            byte[] stackTags = (byte[]) byte[].class.newArrayAt(tagsPtr, stackLength);
            long2[] stackData = (long2[]) long2[].class.newArrayAt(dataPtr, stackLength);
            while((stackIndex = Array.indexOf(TAG_SF_METHOD, stackTags, stackIndex + 1, 0)) >= 0)
            {
                StackTraceElement element = DebugInfo.createStackTraceElement(stackData[stackIndex][1] - 1);
                if(element != null)
                {
                    if(length == result.length) Array.copy(result, 0, result = new StackTraceElement[length << 1 | 1], 0, length);
                    result[length++] = element;
                }
            }
        } catch(Exception exception) {  }
        if(length < result.length) Array.copy(result, 0, result = new StackTraceElement[length], 0, length);
        return result;
    }

    public static StackTraceElement callerCodeTrace() {
        long codePtr = getCallerCodePointer();
        return codePtr == 0 ? null : DebugInfo.createStackTraceElement(codePtr);
    }

    public static Class callerTypeTrace() {
        long codePtr = getCallerCodePointer();
        return codePtr == 0 ? null : DebugInfo.getEnclosingClass(DebugInfo.getDebugInfoEntryPointer(codePtr));
    }

    public static Thread current() {
        long threadId = (long) PlatformServices.getInstance().getCurrentThreadId();
        Thread result;
        Mutex operation = activeOperation;
        operation.lock();
        try
        {
            result = query(threadId);
        } finally
        {
            operation.unlock();
        }
        return result;
    }

    public static UnhandledExceptionHandler getDefaultUnhandledExceptionHandler() { return defaultUnhandledExceptionHandler; }

    package static void initialize() {
        PlatformServices services = PlatformServices.getInstance();
        long2 threadId = services.getCurrentThreadId();
        long mainId = (long) threadId;
        activeLength = 1;
        (activeTable = new ThreadEntry[0x0f]).operator []=((int) (mainId %% 0x0f), new ThreadEntry(mainId, new Thread(threadId, false), null));
        activeOperation = services.newMutex();
        systemUnhandledExceptionHandler = defaultUnhandledExceptionHandler = new SystemUnhandledExceptionHandler();
    }

    package static void clean() {
        PlatformServices services = PlatformServices.getInstance();
        Runtime runtime = Runtime.getInstance();
        Mutex operation = activeOperation;
        operation.lock();
        try
        {
            for(int index = activeTable.length; index-- > 0 && !runtime.isTerminated(); ) for(ThreadEntry prev = null, ThreadEntry curr = activeTable[index]; curr != null; curr = curr.next)
            {
                if(services.isAliveThread(curr.threadId))
                {
                    prev = curr;
                    continue;
                }
                if(prev == null)
                {
                    activeTable[index] = curr.next;
                } else
                {
                    prev.next = curr.next;
                }
                activeLength--;
            }
        } finally
        {
            operation.unlock();
        }
    }

    package static StackTraceElement[] stackTrace(long codePtrOverride) {
        long stackPtr = getCallerStackPointer();
        if(stackPtr == 0) return new StackTraceElement[0];
        int length = 0;
        StackTraceElement[] result = new StackTraceElement[0x0f];
        try
        {
            long dataPtr = getDataPointer();
            long tagsPtr = getTagsPointer();
            int stackLength = getStackLength();
            int stackIndex = (int) (stackPtr - dataPtr >> 4) + 1;
            byte[] stackTags = (byte[]) byte[].class.newArrayAt(tagsPtr, stackLength);
            long2[] stackData = (long2[]) long2[].class.newArrayAt(dataPtr, stackLength);
            for(boolean first = true; (stackIndex = Array.indexOf(TAG_SF_METHOD, stackTags, stackIndex + 1, 0)) >= 0; first = false)
            {
                StackTraceElement element = DebugInfo.createStackTraceElement((first ? codePtrOverride : stackData[stackIndex][1]) - 1);
                if(element != null)
                {
                    if(length == result.length) Array.copy(result, 0, result = new StackTraceElement[length << 1 | 1], 0, length);
                    result[length++] = element;
                }
            }
        } catch(Exception exception) {  }
        if(length < result.length) Array.copy(result, 0, result = new StackTraceElement[length], 0, length);
        return result;
    }

    package static ThreadEvent getEventFor(long threadId) {
        ThreadEvent result;
        Mutex operation = activeOperation;
        operation.lock();
        try
        {
            result = query(threadId).fldEvent;
        } finally
        {
            operation.unlock();
        }
        return result;
    }

    private static void rehash() {
        ThreadEntry[] oldTable = activeTable;
        int oldCapacity = oldTable.length;
        int newCapacity = oldCapacity << 1 | 1;
        if(newCapacity < 0) return;
        ThreadEntry[] newTable = activeTable = new ThreadEntry[newCapacity];
        for(int oldIndex = oldCapacity; oldIndex-- > 0; ) for(ThreadEntry oldEntry = oldTable[oldIndex]; oldEntry != null; )
        {
            int newIndex = (int) (oldEntry.threadId %% newCapacity);
            ThreadEntry newEntry = oldEntry;
            oldEntry = oldEntry.next;
            newEntry.next = newTable[newIndex];
            newTable[newIndex] = newEntry;
        }
    }

    private static void append(long threadId, Thread threadRef) {
        if(activeLength == activeTable.length) rehash();
        int index = (int) (threadId %% activeTable.length);
        activeTable[index] = new ThreadEntry(threadId, threadRef, activeTable[index]);
        activeLength++;
    }

    private static native int getStackLength();

    private static native long getDataPointer();

    private static native long getTagsPointer();

    private static native long getCallerCodePointer();

    private static native long getCallerStackPointer();

    private static Thread query(long threadId) {
        for(int index = (int) (threadId %% activeTable.length), ThreadEntry curr = activeTable[index]; curr != null; curr = curr.next) if(curr.threadId == threadId) return curr.threadRef;
        Thread result = new Thread(threadId, true);
        append(threadId, result);
        return result;
    }

    private boolean fldStarted;
    private long fldThreadId;
    private ThreadEvent fldEvent;
    private final boolean fldExternal;
    private final ThreadHelper fldHelper;

    public (Runnable target) { fldHelper = new ThreadHelper(target == null ? this : target); }

    protected () { fldHelper = new ThreadHelper(this); }

    private (long2 threadId, boolean external) {
        fldStarted = true;
        fldThreadId = (long) threadId;
        fldEvent = new ThreadEvent();
        fldExternal = external;
        fldHelper = new ThreadHelper(threadId);
    }

    public void run() {  }

    public void start() {
        if(isStarted())
        {
            throw new IllegalThreadStateException(package.getResourceString("illegal-state.thread.already-started"));
        }
        fldEvent = new ThreadEvent();
        Mutex operation = activeOperation;
        operation.lock();
        try
        {
            Runtime.getInstance().setMultiThreaded();
            append(fldThreadId = PlatformServices.getInstance().startThread(fldHelper), this);
        } finally
        {
            operation.unlock();
        }
    }

    public void interruptio() { fldHelper.interruptio(); }

    public boolean isInterrupted() { return fldHelper.isInterrupted(); }

    public final void join() throws InterruptedException {
        if(fldExternal)
        {
            throw new IllegalThreadStateException(package.getResourceString("illegal-state.thread.join"));
        }
        fldHelper.join();
    }

    public final void join(long timeInMillis) throws InterruptedException {
        if(fldExternal)
        {
            throw new IllegalThreadStateException(package.getResourceString("illegal-state.thread.join"));
        }
        if(timeInMillis < 0)
        {
            throw new IllegalArgumentException(String.format(package.getResourceString("illegal-argument.negative"), new Object[] { "timeInMillis" }));
        }
        fldHelper.join(timeInMillis, 0);
    }

    public final void join(long timeInMillis, int timeInNanos) throws InterruptedException {
        if(fldExternal)
        {
            throw new IllegalThreadStateException(package.getResourceString("illegal-state.thread.join"));
        }
        if(timeInMillis < 0)
        {
            throw new IllegalArgumentException(String.format(package.getResourceString("illegal-argument.negative"), new Object[] { "timeInMillis" }));
        }
        if(timeInNanos < 0 || timeInNanos > 999999)
        {
            throw new IllegalArgumentException(String.format(package.getResourceString("illegal-argument.out-of-range"), new Object[] { "timeInNanos" }));
        }
        fldHelper.join(timeInMillis, timeInNanos);
    }

    public final boolean isAlive() {
        long threadId = fldThreadId;
        return threadId != 0 && PlatformServices.getInstance().isAliveThread(threadId);
    }

    public final boolean isExternal() { return fldExternal; }

    public final int priority { read = fldHelper.priority, write = setPriority }

    public final String name { read = fldHelper.name, write = setName }

    public final UnhandledExceptionHandler unhandledExceptionHandler { read = fldHelper.fldUnhandledExceptionHandler, write = fldHelper.fldUnhandledExceptionHandler = value }

    protected void setPriority(int newPriority) {
        if(fldExternal)
        {
            throw new IllegalThreadStateException(package.getResourceString("illegal-state.thread.external"));
        }
        if(newPriority < MIN_PRIORITY) newPriority = MIN_PRIORITY;
        if(newPriority > MAX_PRIORITY) newPriority = MAX_PRIORITY;
        fldHelper.priority = newPriority;
    }

    protected void setName(String newName) {
        if(fldExternal)
        {
            throw new IllegalThreadStateException(package.getResourceString("illegal-state.thread.external"));
        }
        if(newName != null)
        {
            int length = newName.length;
            if(length > 15)
            {
                throw new IllegalPropertyValueException(package.getResourceString("illegal-property.thread.name"));
            }
            for(int chr, int index = 0; index < length; index++) if((chr = newName[index]) < 0x20 || chr > 0x7f)
            {
                throw new IllegalPropertyValueException(package.getResourceString("illegal-property.thread.name"));
            }
        }
        fldHelper.name = newName;
    }

    package final long threadId { read = fldThreadId }

    package final InterruptioHandler interruptioHandler { read = fldHelper }

    private native boolean isStarted();
}

class ThreadEntry(Object)
{
    ThreadEntry next;
    final long threadId;
    final Thread threadRef;

    public (long threadId, Thread threadRef, ThreadEntry next) {
        this.next = next;
        this.threadId = threadId;
        this.threadRef = threadRef;
    }
}

class ThreadHelper(Object, Runnable, InterruptioHandler)
{
    long2 fldThreadId;
    UnhandledExceptionHandler fldUnhandledExceptionHandler;
    private boolean fldInterruptedStatus;
    private int fldPriority;
    private int fldNameLength;
    private byte[] fldNameBytes;
    private Waitable fldWaitable;
    private Runnable fldTarget;
    private final Mutex fldInterruptedOperation;
    private final Mutex fldNameOperation;
    private final Object fldJoinMonitor;

    public (long2 threadId) {
        PlatformServices services = PlatformServices.getInstance();
        fldThreadId = threadId;
        if(threadId[1] != 0)
        {
            fldPriority = services.getThreadPriority(threadId);
            fldNameLength = services.getThreadDescription(threadId, fldNameBytes = new byte[16], 0);
        }
        fldInterruptedOperation = services.newMutex();
        fldNameOperation = services.newMutex();
        fldJoinMonitor = new Object();
    }

    public (Runnable target) {
        PlatformServices services = PlatformServices.getInstance();
        fldPriority = Thread.NORM_PRIORITY;
        fldNameBytes = new byte[16];
        fldTarget = target;
        fldInterruptedOperation = services.newMutex();
        fldNameOperation = services.newMutex();
        fldJoinMonitor = new Object();
    }

    public void run() {
        try
        {
            Runnable target;
            try
            {
                try
                {
                    PlatformServices services = PlatformServices.getInstance();
                    long2 threadId = fldThreadId = services.getCurrentThreadId();
                    services.setThreadPriority(threadId, fldPriority);
                    services.setThreadDescription(threadId, fldNameBytes, 0, fldNameLength);
                    services = null;
                    target = fldTarget;
                    fldTarget = null;
                    target.run();
                } catch(Throwable exception)
                {
                    UnhandledExceptionHandler handler = fldUnhandledExceptionHandler;
                    if(handler == null) handler = Thread.getDefaultUnhandledExceptionHandler();
                    handler.unhandledException(Thread.current(), exception);
                }
            } finally
            {
                target = null;
                notifyJoined();
            }
        } catch(Throwable exception) {  }
    }

    public void interruptio() {
        Mutex operation = fldInterruptedOperation;
        operation.lock();
        try
        {
            fldInterruptedStatus = true;
            Waitable waitable = fldWaitable;
            if(waitable != null) waitable.interruptio(fldThreadId[0]);
        } finally
        {
            operation.unlock();
        }
    }

    public void setWaitable(Waitable newWaitable) {
        Mutex operation = fldInterruptedOperation;
        operation.lock();
        try
        {
            if((fldWaitable = newWaitable) == null)
            {
                fldInterruptedStatus = false;
            } else
            {
                if(fldInterruptedStatus) newWaitable.interruptio(fldThreadId[0]);
            }
        } finally
        {
            operation.unlock();
        }
    }

    public boolean isInterrupted() {
        boolean result;
        Mutex operation = fldInterruptedOperation;
        operation.lock();
        try
        {
            result = fldInterruptedStatus;
            fldInterruptedStatus = false;
        } finally
        {
            operation.unlock();
        }
        return result;
    }

    public void join() throws InterruptedException {
        long threadId = 0;
        PlatformServices services = PlatformServices.getInstance();
        synchronized with(fldJoinMonitor)
        {
            while(threadId == 0 && (threadId = fldThreadId[0]) == 0 || services.isAliveThread(threadId)) wait(40);
        }
    }

    public void join(long timeInMillis, int timeInNanos) throws InterruptedException {
        synchronized with(fldJoinMonitor)
        {
            wait(timeInMillis, timeInNanos);
        }
    }

    public int priority { read = fldPriority, write = setPriority }

    public String name { read = getName, write = setName }

    private void notifyJoined() {
        synchronized with(fldJoinMonitor)
        {
            notifyAll();
        }
    }

    private void setPriority(int newPriority) {
        fldPriority = newPriority;
        long2 threadId = fldThreadId;
        if(threadId != 0) PlatformServices.getInstance().setThreadPriority(threadId, newPriority);
    }

    private void setName(String newName) {
        byte[] bytes = fldNameBytes;
        Mutex operation = fldNameOperation;
        operation.lock();
        try
        {
            int length = fldNameLength = newName.length;
            for(int index = 0; index < length; index++) bytes[index] = (byte) newName[index];
            bytes[length] = 0;
            long2 threadId = fldThreadId;
            if(threadId != 0) PlatformServices.getInstance().setThreadDescription(threadId, bytes, 0, length);
        } finally
        {
            operation.unlock();
        }
    }

    private String getName() {
        byte[] bytes = fldNameBytes;
        if(bytes == null) return null;
        Mutex operation = fldNameOperation;
        String result;
        operation.lock();
        try
        {
            int length = fldNameLength;
            long2 threadId = fldThreadId;
            if(threadId != 0) fldNameLength = length = PlatformServices.getInstance().getThreadDescription(threadId, bytes, 0);
            result = new String(bytes, 0, length);
        } finally
        {
            operation.unlock();
        }
        return result;
    }
}