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