Thread.avt

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

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

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

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

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

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

package avt.lang;

import avt.security.*;
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() { Runtime.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)
        {
            Thread current = Thread.current();
            current.fldEvent.wait(timeInMillis, current.fldHelper);
        }
    }

    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)
        {
            Thread current = Thread.current();
            current.fldEvent.wait(timeInMillis, timeInNanos, current.fldHelper);
        }
    }

    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 Runtime.getInstance().activeThreadsCount(); }

    public static Thread[] enumerate() {
        long[] threadsIds = Runtime.getInstance().activeThreadsIds();
        int length = threadsIds.length;
        Mutex operation = activeOperation;
        Thread[] result = new Thread[length];
        operation.lock();
        try
        {
            while(length-- > 0) result[length] = query(threadsIds[length]);
        } finally
        {
            operation.unlock();
        }
        return result;
    }

    public static StackTraceElement[] stackTrace() {
        long stackPointer = getCallerStackPointer();
        if(stackPointer == 0) return new StackTraceElement[0];
        int length = 0;
        StackTraceElement[] result = new StackTraceElement[0x0f];
        try
        {
            long dataPointer = getDataPointer();
            long tagsPointer = getTagsPointer();
            int stackLength = getStackLength();
            int stackIndex = (int) (stackPointer - dataPointer >> 4);
            byte[] stackTags = (byte[]) byte[].class.newArrayAt(tagsPointer, stackLength);
            long2[] stackData = (long2[]) long2[].class.newArrayAt(dataPointer, 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 codePointer = getCallerCodePointer();
        return codePointer == 0 ? null : DebugInfo.createStackTraceElement(codePointer);
    }

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

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

    public static UnhandledExceptionHandler getDefaultUnhandledExceptionHandler() { return defaultUnhandledExceptionHandler; }

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

    package static void clean() {
        Mutex operation = activeOperation;
        Runtime runtime = Runtime.getInstance();
        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(runtime.isAliveThreadId(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 codePointerOverride) {
        long stackPointer = getCallerStackPointer();
        if(stackPointer == 0) return new StackTraceElement[0];
        int length = 0;
        StackTraceElement[] result = new StackTraceElement[0x0f];
        try
        {
            long dataPointer = getDataPointer();
            long tagsPointer = getTagsPointer();
            int stackLength = getStackLength();
            int stackIndex = (int) (stackPointer - dataPointer >> 4) + 1;
            byte[] stackTags = (byte[]) byte[].class.newArrayAt(tagsPointer, stackLength);
            long2[] stackData = (long2[]) long2[].class.newArrayAt(dataPointer, stackLength);
            for(boolean first = true; (stackIndex = Array.indexOf(TAG_SF_METHOD, stackTags, stackIndex + 1, 0)) >= 0; first = false)
            {
                StackTraceElement element = DebugInfo.createStackTraceElement((first ? codePointerOverride : 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 Event getEventFor(long threadId, CallerSecurityContext context) {
        CallerSecurityContext.checkSamePackage(context, Runtime.getInstance().getClass());
        Mutex operation = activeOperation;
        Event result;
        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(Thread threadRef) {
        if(activeLength == activeTable.length) rehash();
        long threadId = threadRef.threadId;
        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(result);
        return result;
    }

    private boolean fldStarted;
    private int fldPriority;
    private Event fldEvent;
    private final boolean fldExternal;
    private final ThreadHelper fldHelper;
    private final Mutex fldSetPriorityOperation;

    public (Runnable target) {
        Runtime runtime = Runtime.getInstance();
        fldStarted = false;
        fldPriority = NORM_PRIORITY;
        fldEvent = null;
        fldExternal = false;
        fldHelper = new ThreadHelper(target == null ? this : target);
        fldSetPriorityOperation = runtime.newMutex();
    }

    protected (): this(null) {  }

    private (long threadId, boolean external) {
        Runtime runtime = Runtime.getInstance();
        fldStarted = true;
        fldPriority = runtime.getThreadIdPriority(threadId);
        fldEvent = runtime.newEvent(threadId);
        fldExternal = external;
        fldHelper = new ThreadHelper() { fldThreadId = threadId };
        fldSetPriorityOperation = runtime.newMutex();
    }

    public void run() {  }

    public void start() {
        if(isStarted())
        {
            throw new IllegalThreadStateException(package.getResourceString("illegal-state.thread.start"));
        }
        Runtime runtime = Runtime.getInstance();
        boolean multiThreaded = runtime.multiThreaded;
        Mutex operation = activeOperation;
        if(multiThreaded) operation.lock();
        try
        {
            long threadId = fldHelper.fldThreadId = runtime.newThreadId(this);
            modPriority(threadId, runtime, multiThreaded);
            fldEvent = runtime.newEvent(threadId);
            append(this);
            runtime.setMultiThreaded();
            runtime.startThreadId(threadId);
        } finally
        {
            if(multiThreaded) operation.unlock();
        }
    }

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

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

    public final void join() throws InterruptedException { 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 = fldHelper.fldThreadId;
        return threadId != 0 && Runtime.getInstance().isAliveThreadId(threadId);
    }

    public final boolean isExternal() { return fldExternal; }

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

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

    protected void setPriority(int newPriority) {
        if(newPriority < MIN_PRIORITY) newPriority = MIN_PRIORITY;
        if(newPriority > MAX_PRIORITY) newPriority = MAX_PRIORITY;
        Runtime runtime = Runtime.getInstance();
        boolean multiThreaded = runtime.multiThreaded;
        Mutex operation = fldSetPriorityOperation;
        if(multiThreaded) operation.lock();
        try
        {
            fldPriority = priority;
            long threadId = fldHelper.fldThreadId;
            if(threadId != 0) runtime.setThreadIdPriority(threadId, newPriority);
        } finally
        {
            if(multiThreaded) operation.unlock();
        }
    }

    package final long threadId { read = fldHelper.fldThreadId }

    package final InterruptioHandler interruptioHandler { read = fldHelper }

    private void execute() { fldHelper.run(); }

    private void modPriority(long threadId, Runtime runtime, boolean multiThreaded) {
        Mutex operation = fldSetPriorityOperation;
        if(multiThreaded) operation.lock();
        try
        {
            runtime.setThreadIdPriority(threadId, fldPriority);
        } finally
        {
            if(multiThreaded) operation.unlock();
        }
    }

    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)
{
    long fldThreadId;
    UnhandledExceptionHandler fldUnhandledExceptionHandler;
    private boolean fldInterruptedStatus;
    private Waitable fldWaitable;
    private Runnable fldTarget;
    private final Mutex fldInterruptedOperation;
    private final Object fldJoinMonitor;

    public (): this(null) {  }

    public (Runnable target) {
        fldInterruptedStatus = false;
        fldWaitable = null;
        fldTarget = target;
        fldInterruptedOperation = Runtime.getInstance().newMutex();
        fldJoinMonitor = new Object();
    }

    public void run() {
        try
        {
            Runnable target;
            try
            {
                try
                {
                    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);
        } 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);
            }
        } 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 = fldThreadId;
        if(threadId == 0) return;
        Runtime runtime = Runtime.getInstance();
        synchronized with(fldJoinMonitor)
        {
            while(runtime.isAliveThreadId(threadId)) wait(40);
        }
    }

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

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