Thread.avt

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

/*
    Исходный код среды исполнения ПВТ-ОО.

    Этот исходный код является частью проекта ПВТ-ОО.

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

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

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

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://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 int count;
    private static Thread[] threads;
    private static Mutex threadsMutex;
    private static EventSet sleepEvents;
    private static UncaughtExceptionHandler systemUncaughtExceptionHandler;
    private static UncaughtExceptionHandler defaultUncaughtExceptionHandler;

    public static void yield() { ThreadEntry.yield(); }

    public static void sleep(long timeInMillis) throws InterruptedException {
        if(timeInMillis < 0L)
        {
            throw new IllegalArgumentException("аргумент timeInMillis не может быть отрицательным");
        }
        if(timeInMillis > 0L)
        {
            sleepEvents.wait(timeInMillis, current().waitableHandler);
        }
    }

    public static void sleep(long timeInMillis, int timeInNanos) throws InterruptedException {
        if(timeInMillis < 0L)
        {
            throw new IllegalArgumentException("аргумент timeInMillis не может быть отрицательным");
        }
        if(timeInNanos < 0 || timeInNanos > 999999)
        {
            throw new IllegalArgumentException("аргумент timeInNanos выходит из диапазона");
        }
        if(timeInMillis > 0L || timeInNanos > 0)
        {
            sleepEvents.wait(timeInMillis, timeInNanos, current().waitableHandler);
        }
    }

    public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) { defaultUncaughtExceptionHandler = handler == null ? systemUncaughtExceptionHandler : handler; }

    public static boolean holdsLock(Object instance) {
        if(instance == null)
        {
            throw new NullPointerException("аргумент instance равен нулевой ссылке");
        }
        Mutex reference = instance.monitor;
        return reference != null && reference.isHold();
    }

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

    public static int activeCount() { return ThreadEntry.countActiveThreads(); }

    public static StackTraceElement[] stackTrace() {
        StackTraceElement[] result = null;
        try
        {
            int tagsLength = CallStack.getTagsLength();
            long dataPointer = CallStack.getDataPointer();
            long tagsPointer = CallStack.getTagsPointer();
            long2[] stackData = (long2[]) long2[].class.newArrayAt(dataPointer, tagsLength);
            byte[] stackTags = (byte[]) byte[].class.newArrayAt(tagsPointer, tagsLength);
            int stackPointer = (int) ((CallStack.getPointer() - dataPointer) >> 4);
            int returnPointer = Array.indexOf(CallStack.TAG_MLEAVE >>> 8, stackTags, stackPointer, 0);
            if(returnPointer >= 0) returnPointer = Array.indexOf(CallStack.TAG_MLEAVE >>> 8, stackTags, returnPointer + 1, 0);
            if(returnPointer < 0)
            {
                result = new StackTraceElement[0];
            } else
            {
                int length = 0;
                int limitPointer = Array.indexOf(CallStack.TAG_ILEAVE >>> 24, stackTags, returnPointer + 1, 0);
                if(limitPointer < 0) limitPointer = tagsLength;
                result = new StackTraceElement[15];
                do
                {
                    StackTraceElement element = DebugInfo.createStackTraceElement(stackData[returnPointer][1] - 1L);
                    if(element != null)
                    {
                        if(length == result.length) Array.copy(result, 0, result = new StackTraceElement[(length << 1) + 1], 0, length);
                        result[length++] = element;
                    }
                } while((returnPointer = Array.indexOf(CallStack.TAG_MLEAVE >>> 8, stackTags, returnPointer + 1, 0)) >= 0 && returnPointer < limitPointer);
                if(length < result.length) Array.copy(result, 0, result = new StackTraceElement[length], 0, length);
            }
        }
        catch(Exception e) {  }
        return result;
    }

    public static Thread[] enumerate() {
        int length = 0;
        long[] activeIds = ThreadEntry.enumerateActiveThreadIds();
        Thread[] result = new Thread[15];
        Mutex globmutex = threadsMutex;
        globmutex.lock();
        try
        {
            for(int i = activeIds.length; i-- > 0; )
            {
                Thread active = query(activeIds[i]);
                if(length == result.length)
                {
                    Array.copy(result, 0, result = new Thread[(length << 1) + 1], 0, length);
                }
                result[length++] = active;
            }
        }
        finally
        {
            globmutex.unlock();
        }
        if(length != result.length)
        {
            Array.copy(result, 0, result = new Thread[length], 0, length);
        }
        return result;
    }

    public static StackTraceElement callerTrace() {
        StackTraceElement result = null;
        try
        {
            int tagsLength = CallStack.getTagsLength();
            long dataPointer = CallStack.getDataPointer();
            long tagsPointer = CallStack.getTagsPointer();
            long2[] stackData = (long2[]) long2[].class.newArrayAt(dataPointer, tagsLength);
            byte[] stackTags = (byte[]) byte[].class.newArrayAt(tagsPointer, tagsLength);
            int stackPointer = (int) ((CallStack.getPointer() - dataPointer) >> 4);
            int returnPointer = Array.indexOf(CallStack.TAG_MLEAVE >>> 8, stackTags, stackPointer, 0);
            if(returnPointer >= 0) returnPointer = Array.indexOf(CallStack.TAG_MLEAVE >>> 8, stackTags, returnPointer + 1, 0);
            if(returnPointer >= 0) result = DebugInfo.createStackTraceElement(stackData[returnPointer][1] - 1L);
        }
        catch(Exception e) {  }
        return result;
    }

    public static Thread current() {
        long threadId = ThreadEntry.getCurrentThreadId();
        Mutex globmutex = threadsMutex;
        Thread result;
        globmutex.lock();
        try
        {
            result = query(threadId);
        }
        finally
        {
            globmutex.unlock();
        }
        return result;
    }

    public static native Throwable suppressedThrowable();

    public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { return defaultUncaughtExceptionHandler; }

    package static void initialize() {
        count = 1;
        (threads = new Thread[15])[0] = new Thread(ThreadEntry.getCurrentThreadId(), false);
        threadsMutex = new Mutex();
        sleepEvents = new EventSet();
        systemUncaughtExceptionHandler = defaultUncaughtExceptionHandler = new SystemUncaughtExceptionHandler();
    }

    package static void clean() {
        Mutex globmutex = threadsMutex;
        globmutex.lock();
        try
        {
            for(int index = count; index-- > 0; ) if(!ThreadEntry.isAliveThread(threads[index].threadId))
            {
                remove(index);
            }
        }
        finally
        {
            globmutex.unlock();
        }
    }

    package static StackTraceElement[] stackTrace(long codePointerOverride) {
        StackTraceElement[] result = null;
        try
        {
            int tagsLength = CallStack.getTagsLength();
            long dataPointer = CallStack.getDataPointer();
            long tagsPointer = CallStack.getTagsPointer();
            long2[] stackData = (long2[]) long2[].class.newArrayAt(dataPointer, tagsLength);
            byte[] stackTags = (byte[]) byte[].class.newArrayAt(tagsPointer, tagsLength);
            int stackPointer = (int) ((CallStack.getPointer() - dataPointer) >> 4);
            int returnPointer = Array.indexOf(CallStack.TAG_MLEAVE >>> 8, stackTags, stackPointer, 0);
            if(returnPointer >= 0) returnPointer = Array.indexOf(CallStack.TAG_MLEAVE >>> 8, stackTags, returnPointer + 1, 0);
            if(returnPointer < 0)
            {
                result = new StackTraceElement[0];
            } else
            {
                boolean first = true;
                int length = 0;
                int limitPointer = Array.indexOf(CallStack.TAG_ILEAVE >>> 24, stackTags, returnPointer + 1, 0);
                if(limitPointer < 0) limitPointer = tagsLength;
                result = new StackTraceElement[15];
                do
                {
                    StackTraceElement element = DebugInfo.createStackTraceElement((first ? codePointerOverride : stackData[returnPointer][1]) - 1L);
                    first = false;
                    if(element != null)
                    {
                        if(length == result.length) Array.copy(result, 0, result = new StackTraceElement[(length << 1) + 1], 0, length);
                        result[length++] = element;
                    }
                } while((returnPointer = Array.indexOf(CallStack.TAG_MLEAVE >>> 8, stackTags, returnPointer + 1, 0)) >= 0 && returnPointer < limitPointer);
                if(length < result.length) Array.copy(result, 0, result = new StackTraceElement[length], 0, length);
            }
        }
        catch(Exception e) {  }
        return result;
    }

    static void finish(ThreadAuxiliary auxiliary) {
        Mutex globmutex = threadsMutex;
        globmutex.lock();
        try
        {
            for(int index = count; index-- > 0; ) if(threads[index].fldAuxiliary == auxiliary)
            {
                remove(index);
                break;
            }
        }
        finally
        {
            globmutex.unlock();
        }
    }

    private static void append(Thread thread) {
        if(count == threads.length)
        {
            Array.copy(threads, 0, threads = new Thread[(count << 1) + 1], 0, count);
        }
        threads[count++] = thread;
    }

    private static void remove(int index) {
        Array.copy(threads, index + 1, threads, index, --count - index);
        threads[count] = null;
    }

    private static int indexOf(long threadId) {
        for(int index = 0; index < count; index++)
        {
            if(threads[index].threadId == threadId) return index;
        }
        return -1;
    }

    private static Thread query(long threadId) {
        int index = indexOf(threadId);
        if(index >= 0) return threads[index];
        Thread result = new Thread(threadId, true);
        append(result);
        return result;
    }


    private boolean fldStarted;
    private int fldPriority;
    private final boolean fldExternal;
    private final ThreadAuxiliary fldAuxiliary;

    public (Runnable target) {
        fldPriority = NORM_PRIORITY;
        fldExternal = false;
        fldAuxiliary = new ThreadAuxiliary(target == null ? this : target);
    }

    protected (): this(null) {  }

    private (long threadId, boolean external) {
        fldStarted = true;
        fldPriority = ThreadEntry.getThreadPriority(threadId);
        fldExternal = external;
        fldAuxiliary = new ThreadAuxiliary() { fldThreadId = threadId };
    }

    public void run() {  }

    public void start() {
        if(isStarted())
        {
            throw new IllegalThreadStateException("поток исполнения уже был запущен раннее");
        }
        Runtime.getInstance().setMultiThreaded();
        ThreadAuxiliary auxiliary = fldAuxiliary;
        Mutex globmutex = threadsMutex;
        globmutex.lock();
        try
        {
            append(this);
            long2 newThreadAttributes = ThreadEntry.createThread(auxiliary, fldPriority);
            long threadId = auxiliary.fldThreadId = newThreadAttributes[0];
            ThreadEntry.startThread(threadId);
        }
        finally
        {
            globmutex.unlock();
        }
    }

    public void interruptio() {
        checkAccess();
        fldAuxiliary.interruptio();
    }

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

    public final void checkAccess() {
        if(fldExternal)
        {
            throw new SecurityException("внешний поток исполнения недоступен для модифицирования");
        }
    }

    public final void join() throws InterruptedException { join(0L, 0); }

    public final void join(long timeInMillis) throws InterruptedException { join(timeInMillis, 0); }

    public final void join(long timeInMillis, int timeInNanos) throws InterruptedException {
        if(fldExternal)
        {
            throw new IllegalThreadStateException("к внешнему потоку исполнения нельзя присоединиться");
        }
        if(timeInMillis < 0L)
        {
            throw new IllegalArgumentException("аргумент timeInMillis не может быть отрицательным");
        }
        if(timeInNanos < 0 || timeInNanos > 999999)
        {
            throw new IllegalArgumentException("аргумент timeInNanos выходит из диапазона");
        }
        fldAuxiliary.join(timeInMillis, timeInNanos);
    }

    public final boolean isAlive() {
        long id = fldAuxiliary.fldThreadId;
        return id != 0L && ThreadEntry.isAliveThread(id);
    }

    public final boolean isExternal() { return fldExternal; }

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

    public final UncaughtExceptionHandler uncaughtExceptionHandler { read = getUncaughtExceptionHandler, write = setUncaughtExceptionHandler }

    protected void setPriority(int priority) {
        checkAccess();
        if(priority < MIN_PRIORITY) priority = MIN_PRIORITY;
        if(priority > MAX_PRIORITY) priority = MAX_PRIORITY;
        fldPriority = priority;
        long threadId = fldAuxiliary.fldThreadId;
        if(threadId != 0L) ThreadEntry.setThreadPriority(threadId, priority);
    }

    package final long threadId { read = getThreadId }

    package final WaitableHandler waitableHandler { read = fldAuxiliary }

    private void setUncaughtExceptionHandler(UncaughtExceptionHandler exceptionHandler) { fldAuxiliary.fldExceptionHandler = exceptionHandler; }

    private native boolean isStarted();

    private long getThreadId() { return fldAuxiliary.fldThreadId; }

    private UncaughtExceptionHandler getUncaughtExceptionHandler() { return fldAuxiliary.fldExceptionHandler; }
}

final class ThreadAuxiliary(Object, Runnable, WaitableHandler)
{
    long fldThreadId;
    UncaughtExceptionHandler fldExceptionHandler;
    private boolean fldInterruptedStatus;
    private boolean fldJoinable;
    private Runnable fldTarget;
    private Waitable fldWaitable;
    private Object fldJoinMonitor;
    private final Mutex fldInterruptedMutex;

    public (): this(null) {  }

    public (Runnable target) {
        fldTarget = target;
        fldInterruptedMutex = new Mutex();
    }

    public void run() {
        Runnable target;
        try
        {
            try
            {
                target = fldTarget;
                fldTarget = null;
                target.run();
            }
            catch(Throwable exception)
            {
                UncaughtExceptionHandler handler = fldExceptionHandler;
                if(handler == null) handler = Thread.getDefaultUncaughtExceptionHandler();
                handler.uncaughtException(Thread.current(), exception);
            }
        }
        finally
        {
            target = null;
            notifyJoined();
            Thread.finish(this);
        }
    }

    public void setWaitable(Waitable reference) {
        Mutex intrmutex = fldInterruptedMutex;
        intrmutex.lock();
        try
        {
            fldWaitable = reference;
            if(reference == null)
            {
                fldInterruptedStatus = false;
            } else
            {
                if(fldInterruptedStatus) reference.interruptio(fldThreadId);
            }
        }
        finally
        {
            intrmutex.unlock();
        }
    }

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

    public void interruptio() {
        Mutex intrmutex = fldInterruptedMutex;
        intrmutex.lock();
        try
        {
            fldInterruptedStatus = true;
            Waitable reference = fldWaitable;
            if(reference != null) reference.interruptio(fldThreadId);
        }
        finally
        {
            intrmutex.unlock();
        }
    }

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

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

    private native boolean isJoinable();

    private Object getJoinMonitor() {
        if(isJoinable())
        {
            Object result;
            while((result = fldJoinMonitor) == null) Thread.yield();
            return result;
        }
        return fldJoinMonitor = new Object();
    }
}

final class CallStack(Object)
{
    public static final int TAG_EMPTY    =       0x00;
    public static final int TAG_INT      =       0x01;
    public static final int TAG_INT2     =       0x12;
    public static final int TAG_INT4     =       0x14;
    public static final int TAG_INT8     =     0x1918;
    public static final int TAG_LONG     =       0x02;
    public static final int TAG_LONG2    =       0x22;
    public static final int TAG_LONG4    =     0x2524;
    public static final int TAG_LONG8    = 0x2b2a2928;
    public static final int TAG_FLOAT    =       0x0f;
    public static final int TAG_FLOAT2   =       0xf2;
    public static final int TAG_FLOAT4   =       0xf4;
    public static final int TAG_FLOAT8   =     0xf9f8;
    public static final int TAG_DOUBLE   =       0x0d;
    public static final int TAG_DOUBLE2  =       0xd2;
    public static final int TAG_DOUBLE4  =     0xd5d4;
    public static final int TAG_DOUBLE8  = 0xdbdad9d8;
    public static final int TAG_BYTE2    =       0xb2;
    public static final int TAG_BYTE4    =       0xb4;
    public static final int TAG_BYTE8    =       0xb8;
    public static final int TAG_SHORT2   =       0x52;
    public static final int TAG_SHORT4   =       0x54;
    public static final int TAG_SHORT8   =       0x58;
    public static final int TAG_REAL     =       0x07;
    public static final int TAG_SUPPRESS =       0x99;
    public static final int TAG_OBJECT   =       0x88;
    public static final int TAG_FLEAVE   =       0xef;
    public static final int TAG_MLEAVE   =     0xcfca;
    public static final int TAG_ILEAVE   = 0xafaeacaa;

    public static native int getTagsLength();

    public static native long getPointer();

    public static native long getDataPointer();

    public static native long getTagsPointer();


    private () {  }
}

service SystemUncaughtExceptionHandler(Object, UncaughtExceptionHandler)
{
    public void uncaughtException(Thread thread, Throwable exception) {
        exception.printStackTrace();
        Runtime.getInstance().exit(1);
    }
}