/*
Реализация среды исполнения языка программирования
Объектно-ориентированный продвинутый векторный транслятор
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;
}
}