Thread.java

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

/*
    Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
    и других спецификаций для функционирования компактных приложений на языке
    Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

    Copyright © 2016–2017, 2019–2023 Малик Разработчик

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

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

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

package java.lang;

import malik.emulator.util.*;

public class Thread extends Object implements Runnable
{
    static final class Monitor extends Object
    {
        private static final String NOT_OWNING = ": текущий поток исполнения не владеет этим блокирующим монитором в настоящий момент.";
        static final String MESSAGE_NOT_OWNING_WAIT = "Object.wait" + NOT_OWNING;
        static final String MESSAGE_NOT_OWNING_NOTIFY = "Object.notify" + NOT_OWNING;
        static final String MESSAGE_NOT_OWNING_NOTIFY_ALL = "Object.notifyAll" + NOT_OWNING;
        static final String MESSAGE_NOT_OWNING_MONITOR_EXIT = "Инструкция monitorexit" + NOT_OWNING;

        private int head;
        private int tail;
        private long ecount;
        private long notifies;
        private long[] ecounts;
        private Thread[] threads;
        private Thread owner;

        public Monitor() {
        }

        public void enter() {
            boolean status = MalikSystem.enterMonopolyAccess();
            try
            {
                Thread owner = this.owner;
                Thread current = Thread.currentThread();
                if(owner == null)
                {
                    this.owner = current;
                    this.ecount = 1L;
                }
                else if(owner == current)
                {
                    this.ecount++;
                }
                else
                {
                    push(current, 1L);
                    current.suspend(false);
                }
            }
            finally
            {
                MalikSystem.leaveMonopolyAccess(status);
            }
        }

        public void wakeup() {
            if(owner == null) pop();
        }

        public void notifyWaiting(boolean all) throws IllegalMonitorStateException {
            boolean status;
            int error = 0;
            status = MalikSystem.enterMonopolyAccess();
            try
            {
                label0:
                {
                    boolean notifying;
                    Thread[] tq;
                    if(owner != Thread.currentThread())
                    {
                        error = 1;
                        break label0;
                    }
                    notifying = false;
                    for(int capacity = (tq = threads) != null ? tq.length : 0, h = head, i = tail; i != h; i = (i + 1) % capacity)
                    {
                        Thread thread;
                        Scheduler.Task task;
                        if((thread = tq[i]).state != WAITING) continue;
                        thread.suspend(false);
                        if((task = thread.suspendTask) != null)
                        {
                            thread.suspendTask = null;
                            task.cancel();
                            task = null;
                        }
                        if(!all)
                        {
                            notifying = true;
                            break;
                        }
                    }
                    if(!all && !notifying) notifies++;
                }
            }
            finally
            {
                MalikSystem.leaveMonopolyAccess(status);
            }
            if(error == 1)
            {
                throw new IllegalMonitorStateException(all ? MESSAGE_NOT_OWNING_NOTIFY_ALL : MESSAGE_NOT_OWNING_NOTIFY, true);
            }
        }

        public void waitNotifying(long millis) throws IllegalMonitorStateException, InterruptedException {
            boolean flag;
            int error = 0;
            flag = MalikSystem.enterMonopolyAccess();
            try
            {
                label0:
                {
                    long ecount;
                    Thread owner;
                    if((owner = this.owner) != Thread.currentThread())
                    {
                        error = 1;
                        break label0;
                    }
                    if(owner.interrupted)
                    {
                        owner.interrupted = false;
                        error = 2;
                        break label0;
                    }
                    if((ecount = notifies) != 0L)
                    {
                        notifies = ecount - 1L;
                        break label0;
                    }
                    if(millis > 0L)
                    {
                        Scheduler.Task task;
                        owner.suspendTask = task = owner.new SuspendTask(this);
                        Scheduler.schedule(task, millis, Scheduler.MONOPOLY);
                        task = null;
                    }
                    ecount = this.ecount;
                    pop();
                    push(owner, ecount);
                    owner.blockedBy = this;
                    owner.suspend(true);
                    owner.blockedBy = null;
                    if(owner.interrupted)
                    {
                        owner.interrupted = false;
                        error = 2;
                        break label0;
                    }
                }
            }
            finally
            {
                MalikSystem.leaveMonopolyAccess(flag);
            }
            switch(error)
            {
            case 1:
                throw new IllegalMonitorStateException(MESSAGE_NOT_OWNING_WAIT, true);
            case 2:
                throw new InterruptedException("Object.wait: поток исполнения был прерван.", true);
            }
        }

        public void exit() throws IllegalMonitorStateException {
            boolean flag;
            int error = 0;
            flag = MalikSystem.enterMonopolyAccess();
            try
            {
                label0:
                {
                    if(owner != Thread.currentThread())
                    {
                        error = 1;
                        break label0;
                    }
                    if(--ecount == 0L) pop();
                }
            }
            finally
            {
                MalikSystem.leaveMonopolyAccess(flag);
            }
            if(error == 1)
            {
                throw new IllegalMonitorStateException(MESSAGE_NOT_OWNING_MONITOR_EXIT, true);
            }
        }

        private void push(Thread thread, long ecount) {
            int qhead;
            int qtail;
            int ocapa;
            long[] oeq;
            Thread[] otq;
            if((oeq = ecounts) == null)
            {
                oeq = ecounts = new long[3];
                otq = threads = new Thread[3];
            } else
            {
                otq = threads;
            }
            if(((qhead = head) + 1) % (ocapa = oeq.length) == (qtail = tail))
            {
                int count;
                int ncapa;
                long[] neq = new long[ncapa = (ocapa << 1) - 1];
                Thread[] ntq = new Thread[ncapa];
                if(qhead < qtail)
                {
                    int quantity = ocapa - qtail;
                    count = qhead + quantity;
                    if(quantity > 0)
                    {
                        MalikSystem.arraycopyf_long(oeq, qtail, neq, 0, quantity);
                        MalikSystem.arraycopyf_object(otq, qtail, ntq, 0, quantity);
                    }
                    if(qhead > 0)
                    {
                        MalikSystem.arraycopyf_long(oeq, 0, neq, quantity, qhead);
                        MalikSystem.arraycopyf_object(otq, 0, ntq, quantity, qhead);
                    }
                } else
                {
                    if((count = qhead - qtail) > 0)
                    {
                        MalikSystem.arraycopyf_long(oeq, qtail, neq, 0, count);
                        MalikSystem.arraycopyf_object(otq, qtail, ntq, 0, count);
                    }
                }
                ecounts = oeq = neq;
                threads = otq = ntq;
                qhead = count;
                ocapa = ncapa;
                tail = 0;
            }
            oeq[qhead] = ecount;
            otq[qhead] = thread;
            head = (qhead + 1) % ocapa;
        }

        private void pop() {
            int h;
            int qhead;
            int qtail;
            long ecount = 0L;
            Thread thread = null;
            if((h = qhead = head) != (qtail = tail))
            {
                int state;
                int capacity;
                long[] eq = ecounts;
                Thread[] tq = threads;
                capacity = eq.length;
                do
                {
                    ecount = eq[qtail];
                    thread = tq[qtail];
                    eq[qtail] = 0L;
                    tq[qtail] = null;
                    qtail = (qtail + 1) % capacity;
                    if((state = thread.state) != WAITING) break;
                    eq[qhead] = ecount;
                    tq[qhead] = thread;
                    qhead = (qhead + 1) % capacity;
                } while(qtail != h);
                head = qhead;
                tail = qtail;
                if(state == WAITING)
                {
                    ecount = 0L;
                    thread = null;
                }
            }
            this.owner = thread;
            this.ecount = ecount;
            if(thread != null) thread.resume();
        }
    }

    static final int CREATED = 0;
    static final int RUNNING = 1;
    static final int WAITING = 2;
    static final int SUSPENDED = 3;
    static final int TERMINATED = 4;
    public static final int MIN_PRIORITY = 1;
    public static final int NORM_PRIORITY = 5;
    public static final int MAX_PRIORITY = 10;
    private static final int STACK_RESERVED = 0x0800;

    private static int LAST_NUM;
    static final int MAIN_THREAD_ID;
    private static final Thread[] THREADS;

    static {
        MAIN_THREAD_ID = MalikSystem.getCurrentThreadID();
        THREADS = new Thread[getMaximumThreads()];
    }

    public static void yield() {
        /*
         * Потоки исполнения переключаются автоматически. За время выполнения этого метода произойдёт хотя бы одно переключение.
         */
        int i = 0;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i++;
        i = i + 1;
    }

    public static void sleep(long millis) throws InterruptedException {
        boolean status;
        int error = 0;
        status = MalikSystem.enterMonopolyAccess();
        try
        {
            label0:
            {
                Thread thread;
                if((thread = Thread.currentThread()).interrupted)
                {
                    thread.interrupted = false;
                    error = 1;
                    break label0;
                }
                if(millis <= 0L) break label0;
                Scheduler.Task task;
                thread.resumeTask = task = thread.new ResumeTask();
                Scheduler.schedule(task, millis, Scheduler.MONOPOLY);
                task = null;
                thread.suspend(false);
                if(thread.interrupted)
                {
                    thread.interrupted = false;
                    error = 1;
                    break label0;
                }
            }
        }
        finally
        {
            MalikSystem.leaveMonopolyAccess(status);
        }
        if(error == 1)
        {
            throw new InterruptedException("Thread.sleep: поток исполнения был прерван.");
        }
    }

    public static int activeCount() {
        int result = 0;
        for(int i = THREADS.length; i-- > 0; ) if(THREADS[i] != null) result++;
        return result;
    }

    public static Thread currentThread() {
        return THREADS[MalikSystem.getCurrentThreadID()];
    }

    static void completeInit() {
        THREADS[MAIN_THREAD_ID] = new Thread(NORM_PRIORITY, MAIN_THREAD_ID);
    }

    static void waitForInterrupt() throws InterruptedException {
        Thread thread;
        if((thread = Thread.currentThread()).interrupted)
        {
            thread.interrupted = false;
            throw new InterruptedException("Thread.waitForInterrupt: поток исполнения был прерван.");
        }
        MalikSystem.syscall(0L, 0x000e);
        if(thread.interrupted)
        {
            thread.interrupted = false;
            throw new InterruptedException("Thread.waitForInterrupt: поток исполнения был прерван.");
        }
    }

    private static void writeStackLimit(int address) {
        MalikSystem.setIntAt(address + 0x0c, MalikSystem.STACKITEM_OVERFLOW);
    }

    private static int nextThreadNumber() {
        int result;
        synchronized(THREADS)
        {
            result = ++LAST_NUM;
        }
        return result;
    }

    private static int getMaximumThreads() {
        return ((int) (MalikSystem.syscall(0L, 0x000f) >> 32) & 0xff) + 1;
    }

    private class SuspendTask extends Scheduler.Task
    {
        private final Monitor monitor;

        public SuspendTask(Monitor monitor) {
            this.monitor = monitor;
        }

        public void run() {
            Thread owner;
            if((owner = Thread.this).state == WAITING)
            {
                owner.suspendTask = null;
                owner.suspend(false);
                monitor.wakeup();
            }
        }

        protected void cancelled() {
            monitor.wakeup();
        }
    }

    private class ResumeTask extends Scheduler.Task
    {
        public ResumeTask() {
        }

        public void run() {
            Thread owner;
            if((owner = Thread.this).state == SUSPENDED)
            {
                owner.resumeTask = null;
                owner.resume();
            }
        }
    }

    private boolean stackTrace;
    boolean interrupted;
    private int id;
    int state;
    private int priority;
    private int stackBottom;
    Scheduler.Task resumeTask;
    Scheduler.Task suspendTask;
    Monitor blockedBy;
    private final Runnable target;
    private final String name;
    private final Object monitor;

    public Thread() {
        this(null, null);
    }

    public Thread(String name) {
        this(null, name);
    }

    public Thread(Runnable target) {
        this(target, null);
    }

    public Thread(Runnable target, String name) {
        if(name == null || name.length() <= 0) name = "Поток исполнения #".concat(Integer.toString(nextThreadNumber()));
        this.id = -1;
        this.state = CREATED;
        this.priority = NORM_PRIORITY;
        this.stackBottom = 0;
        this.target = target;
        this.name = name;
        this.monitor = new Object();
    }

    private Thread(int priority, int id) {
        int address;
        long answer;
        if(priority < MIN_PRIORITY || priority > MAX_PRIORITY)
        {
            throw new IllegalArgumentException("Thread: аргумент priority выходит из диапазона.");
        }
        writeStackLimit((address = (int) (answer = MalikSystem.syscall(0L, 0x0006))) + STACK_RESERVED);
        this.id = id;
        this.state = RUNNING;
        this.priority = priority;
        this.stackBottom = address + (int) (answer >> 32) + 1;
        this.target = null;
        this.name = "Главный поток исполнения";
        this.monitor = new Object();
        MalikSystem.syscall(id, priority << 1, 0x0009);
    }

    public String toString() {
        return (new StringBuilder()).append("Поток исполнения[").append(name).append(", приоритет=").append(priority).append("]").toString();
    }

    public void run() {
    }

    public void start() {
        boolean flag;
        int error = 0;
        flag = MalikSystem.enterMonopolyAccess();
        try
        {
            label0:
            {
                int id;
                int address;
                long answer;
                if(state != CREATED)
                {
                    error = 1;
                    break label0;
                }
                if((answer = MalikSystem.syscall(MalikSystem.getMethodAddress("java/lang/Thread.execute()V"), MalikSystem.convertToReference(this), 0x0000)) == -1L)
                {
                    error = 2;
                    break label0;
                }
                writeStackLimit((address = (int) (answer >> 32)) + STACK_RESERVED);
                this.id = id = (int) answer & 0xff;
                this.state = RUNNING;
                this.stackBottom = address + ((int) answer & -0x0100) + 0x0100;
                MalikSystem.syscall(id, priority << 1, 0x0009);
                MalikSystem.syscall((long) id, 0x0003);
                Memory.setMultiThreaded();
            }
        }
        finally
        {
            MalikSystem.leaveMonopolyAccess(flag);
        }
        switch(error)
        {
        case 1:
            throw new IllegalThreadStateException((new StringBuilder()).append("Thread.start: поток исполнения \"").append(name).append("\" уже был запущен раннее.").toString());
        case 2:
            throw new OutOfThreadsError("Невозможно создать новый поток исполнения: выполняется максимальное количество потоков исполнения.");
        }
    }

    public void interrupt() {
        boolean status = MalikSystem.enterMonopolyAccess();
        try
        {
            label0:
            {
                Monitor monitor;
                Scheduler.Task task;
                interrupted = true;
                if((task = resumeTask) != null)
                {
                    /* Thread.sleep(long) */
                    resumeTask = null;
                    resume();
                    task.cancel();
                    break label0;
                }
                if((task = suspendTask) != null)
                {
                    /* Object.wait(long) */
                    /* Object.wait(long, int) */
                    suspendTask = null;
                    suspend(false);
                    task.cancel();
                    break label0;
                }
                if((monitor = blockedBy) != null)
                {
                    /* Object.wait() */
                    suspend(false);
                    monitor.wakeup();
                    break label0;
                }
            }
        }
        finally
        {
            MalikSystem.leaveMonopolyAccess(status);
        }
    }

    public final void join() throws InterruptedException {
        Object monitor;
        synchronized(monitor = this.monitor)
        {
            for(; isAlive(); monitor.wait(1000L));
        }
    }

    public final void setPriority(int priority) {
        boolean status;
        if(priority < MIN_PRIORITY || priority > MAX_PRIORITY)
        {
            throw new IllegalArgumentException("Thread.setPriority: аргумент priority выходит из диапазона.");
        }
        status = MalikSystem.enterMonopolyAccess();
        try
        {
            int state;
            this.priority = priority;
            if((state = this.state) > CREATED && state < TERMINATED) MalikSystem.syscall(id, priority << 1, 0x0009);
        }
        finally
        {
            MalikSystem.leaveMonopolyAccess(status);
        }
    }

    public final boolean isAlive() {
        int state;
        return (state = this.state) > CREATED && state < TERMINATED;
    }

    public final int getPriority() {
        return priority;
    }

    public final String getName() {
        return name;
    }

    final void execute() {
        int id;
        Throwable exitThrowable;
        THREADS[id = this.id] = this;
        exitThrowable = null;
        try
        {
            Runnable target;
            ((target = this.target) == null ? this : target).run();
        }
        catch(Throwable e)
        {
            exitThrowable = e;
        }
        MalikInterrupt.disable();
        ThreadTerminationListenerCollection.instance.notifyListeners(this, exitThrowable);
        THREADS[id] = null;
        state = TERMINATED;
    }

    final void resume() {
        switch(state)
        {
        default:
            return;
        case SUSPENDED:
        case WAITING:
            state = RUNNING;
            if(!stackTrace) MalikSystem.syscall((long) id, 0x0003);
            return;
        }
    }

    final void suspend(boolean wait) {
        switch(state)
        {
        default:
            return;
        case RUNNING:
            state = wait ? WAITING : SUSPENDED;
            if(!stackTrace) MalikSystem.syscall((long) id, 0x0002);
            return;
        case SUSPENDED:
        case WAITING:
            state = wait ? WAITING : SUSPENDED;
            return;
        }
    }

    final void enterStackTrace() {
        boolean status;
        int error = 0;
        status = MalikSystem.enterMonopolyAccess();
        try
        {
            label0:
            {
                if(id != MalikSystem.getCurrentThreadID())
                {
                    switch(state)
                    {
                    default:
                        break;
                    case CREATED:
                        error = 1;
                        break label0;
                    case RUNNING:
                        MalikSystem.syscall((long) id, 0x0002);
                        break;
                    case TERMINATED:
                        error = 2;
                        break label0;
                    }
                }
                stackTrace = true;
            }
        }
        finally
        {
            MalikSystem.leaveMonopolyAccess(status);
        }
        switch(error)
        {
        case 1:
            throw new IllegalThreadStateException("StackTracer.trace: нельзя выполнить трассировку стака для незапущенного потока исполнения.", true);
        case 2:
            throw new IllegalThreadStateException("StackTracer.trace: нельзя выполнить трассировку стака для завершившегося потока исполнения.", true);
        }
    }

    final void leaveStackTrace() {
        boolean flag = MalikSystem.enterMonopolyAccess();
        try
        {
            stackTrace = false;
            if(id != MalikSystem.getCurrentThreadID() && state == RUNNING) MalikSystem.syscall((long) id, 0x0003);
        }
        finally
        {
            MalikSystem.leaveMonopolyAccess(flag);
        }
    }

    final int getStackBottom() {
        return stackBottom;
    }

    final int getID() {
        return id;
    }
}