/*
Реализация спецификаций 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;
}
}