Files
fabric-carpet/src/main/java/carpet/script/language/Threading.java
T
2023-02-22 02:33:03 +01:00

192 lines
7.0 KiB
Java

package carpet.script.language;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.argument.FunctionArgument;
import carpet.script.exception.ExitStatement;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.BooleanValue;
import carpet.script.value.NumericValue;
import carpet.script.value.ThreadValue;
import carpet.script.value.Value;
public class Threading
{
public static void apply(Expression expression)
{
expression.addFunctionWithDelegation("task", -1, false, false, (c, t, expr, tok, lv) ->
{
if (lv.isEmpty())
{
throw new InternalExpressionException("'task' requires at least function to call as a parameter");
}
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, false, true);
ThreadValue thread = new ThreadValue(Value.NULL, functionArgument.function, expr, tok, c, functionArgument.checkedArgs());
Thread.yield();
return thread;
});
expression.addFunctionWithDelegation("task_thread", -1, false, false, (c, t, expr, tok, lv) ->
{
if (lv.size() < 2)
{
throw new InternalExpressionException("'task' requires at least function to call as a parameter");
}
Value queue = lv.get(0);
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, false, true);
ThreadValue thread = new ThreadValue(queue, functionArgument.function, expr, tok, c, functionArgument.checkedArgs());
Thread.yield();
return thread;
});
expression.addContextFunction("task_count", -1, (c, t, lv) ->
(!lv.isEmpty()) ? new NumericValue(c.host.taskCount(lv.get(0))) : new NumericValue(c.host.taskCount()));
expression.addUnaryFunction("task_value", v ->
{
if (!(v instanceof final ThreadValue tv))
{
throw new InternalExpressionException("'task_value' could only be used with a task value");
}
return tv.getValue();
});
expression.addUnaryFunction("task_join", v ->
{
if (!(v instanceof final ThreadValue tv))
{
throw new InternalExpressionException("'task_join' could only be used with a task value");
}
return tv.join();
});
expression.addLazyFunction("task_dock", 1, (c, t, lv) ->
// pass through placeholder
// implmenetation should dock the task on the main thread.
lv.get(0)
);
expression.addUnaryFunction("task_completed", v ->
{
if (!(v instanceof final ThreadValue tv))
{
throw new InternalExpressionException("'task_completed' could only be used with a task value");
}
return BooleanValue.of(tv.isFinished());
});
// lazy cause expr is evaluated in the same type
expression.addLazyFunction("synchronize", (c, t, lv) ->
{
if (lv.isEmpty())
{
throw new InternalExpressionException("'synchronize' require at least an expression to synchronize");
}
Value lockValue = Value.NULL;
int ind = 0;
if (lv.size() == 2)
{
lockValue = lv.get(0).evalValue(c);
ind = 1;
}
synchronized (c.host.getLock(lockValue))
{
Value ret = lv.get(ind).evalValue(c, t);
return (ct, tt) -> ret;
}
});
// lazy since exception expression is very conditional
expression.addLazyFunction("sleep", (c, t, lv) ->
{
long time = lv.isEmpty() ? 0L : NumericValue.asNumber(lv.get(0).evalValue(c)).getLong();
boolean interrupted = false;
try
{
if (Thread.interrupted())
{
interrupted = true;
}
if (time > 0)
{
Thread.sleep(time);
}
Thread.yield();
}
catch (InterruptedException ignored)
{
interrupted = true;
}
if (interrupted)
{
Value exceptionally = Value.NULL;
if (lv.size() > 1)
{
exceptionally = lv.get(1).evalValue(c);
}
throw new ExitStatement(exceptionally);
}
return (cc, tt) -> new NumericValue(time); // pass through for variables
});
expression.addLazyFunction("yield", (c, t, lv) ->
{
if (c.getThreadContext() == null)
{
throw new InternalExpressionException("'yield' can only be used in a task");
}
if (lv.isEmpty())
{
throw new InternalExpressionException("'yield' requires at least one argument");
}
boolean lock = lv.size() > 1 && lv.get(1).evalValue(c, Context.BOOLEAN).getBoolean();
Value value = lv.get(0).evalValue(c);
Value ret = c.getThreadContext().ping(value, lock);
return (cc, tt) -> ret;
});
expression.addLazyFunction("task_send", 2, (c, t, lv) ->
{
Value threadValue = lv.get(0).evalValue(c);
if (!(threadValue instanceof ThreadValue thread))
{
throw new InternalExpressionException("'task_next' requires a task value");
}
if (!thread.isCoroutine)
{
throw new InternalExpressionException("'task_next' requires a coroutine task value");
}
Value ret = lv.get(1).evalValue(c);
thread.send(ret);
return (cc, tt) -> Value.NULL;
});
expression.addLazyFunction("task_await", 1, (c, t, lv) ->
{
Value threadValue = lv.get(0).evalValue(c);
if (!(threadValue instanceof ThreadValue thread))
{
throw new InternalExpressionException("'task_await' requires a task value");
}
if (!thread.isCoroutine)
{
throw new InternalExpressionException("'task_await' requires a coroutine task value");
}
Value ret = thread.next();
return ret == Value.EOL ? ((cc, tt) -> Value.NULL) : ((cc, tt) -> ret);
});
expression.addLazyFunction("task_ready", 1, (c, t, lv) ->
{
Value threadValue = lv.get(0).evalValue(c);
if (!(threadValue instanceof ThreadValue thread))
{
throw new InternalExpressionException("'task_ready' requires a task value");
}
boolean ret = thread.isCoroutine && thread.hasNext();
return (cc, tt) -> BooleanValue.of(ret);
});
}
}