Commit 6d022bce authored by Kunshan Wang's avatar Kunshan Wang

Implemented and tested threadlocal

parent 6a609132
......@@ -15,6 +15,8 @@ object CommInsts extends SimpleNamespace[CommInst] {
commInst(0x202, "@uvm.kill_stack")
commInst(0x203, "@uvm.thread_exit", isTerminator = true)
commInst(0x204, "@uvm.current_stack")
commInst(0x205, "@uvm.set_threadlocal")
commInst(0x206, "@uvm.get_threadlocal")
commInst(0x211, "@uvm.tr64.is_fp")
commInst(0x212, "@uvm.tr64.is_int")
......
......@@ -499,14 +499,22 @@ class MuCtx(_mutator: Mutator)(
}
}
private def getThreadNotNull(thread: MuThreadRefValue): InterpreterThread = {
thread.vb.thread.getOrElse {
throw new UvmRuntimeException("Thread argument cannot be a NULL threadref value.")
}
}
/** Create a Mu thread and bind it to a Mu stack. */
def newThread(stack: MuStackRefValue, htr: HowToResume): MuThreadRefValue = {
def newThread(stack: MuStackRefValue, threadLocal: Option[MuRefValue], htr: HowToResume): MuThreadRefValue = {
val sv = getStackNotNull(stack)
val itprHtr = htr match {
case HowToResume.PassValues(values) => ItprHowToResume.PassValues(values.map(_.vb))
case HowToResume.ThrowExc(exc) => ItprHowToResume.ThrowExc(exc.vb.objRef)
}
val thr = microVM.threadStackManager.newThread(sv, 0L, itprHtr) // TODO: FIXME!!
val threadLocalAddr = threadLocal.map(tl => tl.vb.objRef).getOrElse(0L)
val thr = microVM.threadStackManager.newThread(sv, threadLocalAddr, itprHtr)
val nb = BoxThread(Some(thr))
addHandle(MuThreadRefValue(InternalTypes.THREADREF, nb))
......@@ -518,6 +526,23 @@ class MuCtx(_mutator: Mutator)(
sv.kill()
}
/** Set the thread-local object reference */
def setThreadlocal(thread: MuThreadRefValue, threadLocal: MuRefValue): Unit = {
val th = getThreadNotNull(thread)
val threadLocalAddr = threadLocal.vb.objRef
th.threadLocal.objRef = threadLocalAddr
}
/** Get the thread-local object reference */
def getThreadlocal(thread: MuThreadRefValue): MuRefValue = {
val th = getThreadNotNull(thread)
val threadLocalAddr = th.threadLocal.objRef
val nb = BoxRef(threadLocalAddr)
addHandle(MuRefValue(InternalTypes.REF_VOID, nb))
}
private def getCursorNotNull(cursor: MuFCRefValue): FrameCursor = {
cursor.vb.cursor.getOrElse {
......
......@@ -144,6 +144,8 @@ object TypeInferer {
case "@uvm.kill_stack" => Seq()
case "@uvm.thread_exit" => Seq()
case "@uvm.current_stack" => Seq(STACKREF)
case "@uvm.set_threadlocal" => Seq()
case "@uvm.get_threadlocal" => Seq(REF_VOID)
case "@uvm.tr64.is_fp" => Seq(I1)
case "@uvm.tr64.is_int" => Seq(I1)
case "@uvm.tr64.is_ref" => Seq(I1)
......
......@@ -56,6 +56,17 @@ trait CommInstExecutor extends InterpreterActions with ObjectPinner {
continueNormally()
}
case "@uvm.set_threadlocal" => {
val Seq(tl) = argList
threadLocal copyFrom tl
continueNormally()
}
case "@uvm.get_threadlocal" => {
results(0) copyFrom threadLocal
continueNormally()
}
// 64-bit Tagged Reference
case "@uvm.tr64.is_fp" => {
......
......@@ -522,15 +522,17 @@ trait InstructionExecutor extends InterpreterActions with CommInstExecutor {
val newStack = stack.asStack.getOrElse {
throw new UvmRuntimeException(ctx + "Attempt to bind to a NULL stack.")
}
val threadLocalAddr = threadLocal.map(tl => tl.asRef).getOrElse(0L)
val newThread = newStackAction match {
case PassValues(argTys, args) => {
val argBoxes = args.map(boxOf)
microVM.threadStackManager.newThread(newStack, 0L, HowToResume.PassValues(argBoxes)) // TODO: FIXME
microVM.threadStackManager.newThread(newStack, threadLocalAddr, HowToResume.PassValues(argBoxes))
}
case ThrowExc(exc) => {
val excAddr = exc.asRef
microVM.threadStackManager.newThread(newStack, 0L, HowToResume.ThrowExc(excAddr)) // TODO: FIXME
microVM.threadStackManager.newThread(newStack, threadLocalAddr, HowToResume.ThrowExc(excAddr))
}
}
results(0).asThread = Some(newThread)
......
......@@ -29,11 +29,11 @@ object HowToResume {
* <p>
* @param id The thread ID
* @param initialStack The initial stack it binds to
* @param threadLocal The initial thread-local object reference
* @param initialThreadLocal The initial thread-local object reference
* @param mutator The Mutator object, for memory management
* @param htr How to resume. Either pass value or throw exception.
*/
class InterpreterThread(val id: Int, initialStack: InterpreterStack, threadLocal: Long, htr: HowToResume, val mutator: Mutator)(
class InterpreterThread(val id: Int, initialStack: InterpreterStack, initialThreadLocal: Long, htr: HowToResume, val mutator: Mutator)(
implicit protected val microVM: MicroVM) extends InstructionExecutor with HasID {
import InterpreterThread._
......@@ -43,6 +43,8 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, threadLocal
override def curThread = this
// Initialisation
threadLocal.asRef = initialThreadLocal
htr match {
case HowToResume.PassValues(values) => rebindPassValues(initialStack, values)
......
......@@ -278,7 +278,7 @@ object NativeMuCtx {
// NOTE: parameter exc must not be a MuValue because this parameter is ignored when htr is MU_REBIND_PASS_VALUE.
// Setting the type to MuValue (or MuRefValue) will force the exposer to eagerly resolve the underlying actual MuValue.
def new_thread(ctx: MuCtx, stack: MuStackRefValue, htr: MuHowToResume, vals: MuValueFakArrayPtr, nvals: Int, exc: MuValueFak): MuValueFak = {
def new_thread(ctx: MuCtx, stack: MuStackRefValue, threadLocal: Option[MuRefValue], htr: MuHowToResume, vals: MuValueFakArrayPtr, nvals: Int, exc: MuValueFak): MuValueFak = {
val scalaHtr = htr match {
case MU_REBIND_PASS_VALUES => {
val values = for (i <- 0L until nvals) yield {
......@@ -294,10 +294,12 @@ object NativeMuCtx {
HowToResume.ThrowExc(excVal)
}
}
val rv = ctx.newThread(stack, scalaHtr)
val rv = ctx.newThread(stack, threadLocal, scalaHtr)
exposeMuValue(ctx, rv)
}
def kill_stack(ctx: MuCtx, stack: MuStackRefValue): Unit = ctx.killStack(stack)
def set_threadlocal(ctx: MuCtx, threadLocal:MuRefValue): Unit = ???
def get_threadlocal(ctx: MuCtx): MuValueFak = ???
// Frame cursor operations
def new_cursor(ctx: MuCtx, stack: MuStackRefValue): MuValueFak = exposeMuValue(ctx, ctx.newCursor(stack))
......
......@@ -40,7 +40,7 @@ object FactorialFromRPython extends App {
})
val sta = ctx.newStack(m)
val thr = ctx.newThread(sta, PassValues(Seq()))
val thr = ctx.newThread(sta, None, PassValues(Seq()))
microVM.execute() // run until all threads stop
......
......@@ -63,7 +63,7 @@ object Interact extends App {
val fortyTwo = ctx.handleFromInt(42, 64) // #8
val th = ctx.newThread(st, HowToResume.PassValues(Seq(fortyTwo))) // #9
val th = ctx.newThread(st, None, HowToResume.PassValues(Seq(fortyTwo))) // #9
// Close the context
ctx.closeContext() // #10
......
......@@ -86,10 +86,11 @@ abstract class UvmBundleTesterBase extends FlatSpec with Matchers {
}
}
def testFunc(ctx: MuCtx, func: MuFuncRefValue, args: Seq[MuValue])(handler: TrapHandlerFunction): Unit = {
def testFunc(ctx: MuCtx, func: MuFuncRefValue, args: Seq[MuValue], threadLocal: Option[MuRefValue]=None
)(handler: TrapHandlerFunction): Unit = {
microVM.setTrapHandler(new MockTrapHandler(handler))
val hStack = ctx.newStack(func)
val hThread = ctx.newThread(hStack, HowToResume.PassValues(args))
val hThread = ctx.newThread(hStack, threadLocal, HowToResume.PassValues(args))
microVM.execute()
}
......
......@@ -1587,4 +1587,83 @@ class UvmInterpreterSpec extends UvmBundleTesterBase {
ctx.closeContext()
}
"NEWTHREAD" should "set the thread-local objref" in {
val ctx = microVM.newContext()
val func = ctx.handleFromFunc("@newthread")
val theTl = ctx.newFixed("@i64")
testFunc(ctx, func, Seq(theTl)) { (ctx, th, st, wp) =>
val cursor = ctx.newCursor(st)
nameOf(ctx.curInst(cursor)) match {
case "@threadslave.v1.entry.trap" => {
val Seq(tl: MuRefValue) = ctx.dumpKeepalives(cursor)
ctx.closeCursor(cursor)
tl.vb.asRef shouldEqual theTl.vb.asRef
val hThreadLocal = ctx.getThreadlocal(th)
ctx.refEq(hThreadLocal, tl) shouldBe true
Rebind(st, PassValues(Seq()))
}
case "@threadslave.v1.entry.trap2" => {
val Seq(tl2: MuRefValue, tl3: MuRefValue) = ctx.dumpKeepalives(cursor)
ctx.closeCursor(cursor)
ctx.refEq(tl2, tl3) shouldBe true
val hThreadLocal = ctx.getThreadlocal(th)
ctx.refEq(hThreadLocal, tl2) shouldBe true
Rebind(st, PassValues(Seq()))
}
}
}
ctx.closeContext()
}
"TRAP" should "be able to set the thread-local objref" in {
val ctx = microVM.newContext()
val func = ctx.handleFromFunc("@threadslave2")
val theTl1 = ctx.newFixed("@i64")
val theTl2 = ctx.newFixed("@i64")
testFunc(ctx, func, Seq(theTl1, theTl2), threadLocal=Some(theTl1)) { (ctx, th, st, wp) =>
val cursor = ctx.newCursor(st)
nameOf(ctx.curInst(cursor)) match {
case "@threadslave2.v1.entry.trap" => {
val Seq(tl1: MuRefValue, tl2: MuRefValue, tlcur: MuRefValue) = ctx.dumpKeepalives(cursor)
ctx.closeCursor(cursor)
ctx.refEq(tlcur, tl1) shouldBe true
val hThreadLocal = ctx.getThreadlocal(th)
ctx.refEq(hThreadLocal, tlcur) shouldBe true
ctx.setThreadlocal(th, tl2)
Rebind(st, PassValues(Seq()))
}
case "@threadslave2.v1.entry.trap2" => {
val Seq(tl1: MuRefValue, tl2: MuRefValue, tlcur: MuRefValue) = ctx.dumpKeepalives(cursor)
ctx.closeCursor(cursor)
ctx.refEq(tlcur, tl2) shouldBe true
val hThreadLocal = ctx.getThreadlocal(th)
ctx.refEq(hThreadLocal, tlcur) shouldBe true
Rebind(st, PassValues(Seq()))
}
}
}
ctx.closeContext()
}
}
\ No newline at end of file
......@@ -165,7 +165,7 @@ class UvmOSRTests extends UvmBundleTesterBase {
for (f <- funcs.tail) {
ctx.pushFrame(hStack, f)
}
val hThread = ctx.newThread(hStack, uvm.refimpl.HowToResume.PassValues(args))
val hThread = ctx.newThread(hStack, None, uvm.refimpl.HowToResume.PassValues(args))
microVM.execute()
}
......
......@@ -985,3 +985,37 @@
COMMINST @uvm.thread_exit
}
.funcdef @threadslave VERSION %v1 <@v_v> {
%entry():
%tl = COMMINST @uvm.get_threadlocal
[%trap] TRAP <> KEEPALIVE (%tl)
%tl2 = NEW <@i64>
COMMINST @uvm.set_threadlocal (%tl2)
%tl3 = COMMINST @uvm.get_threadlocal
[%trap2] TRAP <> KEEPALIVE (%tl2 %tl3)
COMMINST @uvm.thread_exit
}
.funcsig @newthread.sig = (@refvoid) -> ()
.funcdef @newthread VERSION %v1 <@newthread.sig> {
%entry(<@refvoid> %tl):
%st = COMMINST @uvm.new_stack <[@v_v]> (@threadslave)
%th = NEWTHREAD %st THREADLOCAL(%tl) PASS_VALUES <> ()
COMMINST @uvm.thread_exit
}
.funcsig @threadslave2.sig = (@refvoid @refvoid) -> ()
.funcdef @threadslave2 VERSION %v1 <@threadslave2.sig> {
%entry(<@refvoid> %tl1 <@refvoid> %tl2):
%tlcur1 = COMMINST @uvm.get_threadlocal
[%trap] TRAP <> KEEPALIVE (%tl1 %tl2 %tlcur1)
%tlcur2 = COMMINST @uvm.get_threadlocal
[%trap2] TRAP <> KEEPALIVE (%tl1 %tl2 %tlcur2)
COMMINST @uvm.thread_exit
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment