Commit 86493917 authored by Kunshan Wang's avatar Kunshan Wang

WIP: callback

parent c589e5c8
......@@ -79,4 +79,11 @@ class MicroVM(heapSize: Word = MicroVM.DEFAULT_HEAP_SIZE,
* Given an ID, get the name of an identified entity.
*/
def nameOf(id: Int): String = globalBundle.allNs(id).name.get
/**
* Execute. This is the external pusher of the execution.
*/
def execute(): Unit = {
threadStackManager.execute()
}
}
\ No newline at end of file
......@@ -780,6 +780,8 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
val argBoxes = argList.map(boxOf)
val retBox = boxOf(i)
microVM.threadStackManager.threadCallingNative = Some(this)
microVM.nativeCallHelper.callNative(sig, addr, argBoxes, retBox)
continueNormally()
......
......@@ -12,6 +12,10 @@ object ThreadStackManager {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
}
/**
* The manager of all Mu threads and stacks. Also responsible for the actual execution of Mu IR code, i.e. as the "boss"
* of all InterpreterThread instances.
*/
class ThreadStackManager(implicit microVM: MicroVM) {
import ThreadStackManager._
......@@ -22,7 +26,7 @@ class ThreadStackManager(implicit microVM: MicroVM) {
def getStackByID(id: Int): Option[InterpreterStack] = stackRegistry.get(id)
def getThreadByID(id: Int): Option[InterpreterThread] = threadRegistry.get(id)
def iterateAllLiveStacks: Iterable[InterpreterStack] = stackRegistry.values.filter(_.state != StackState.Dead)
def iterateAllLiveThreads: Iterable[InterpreterThread] = threadRegistry.values.filter(_.isRunning)
......@@ -37,6 +41,11 @@ class ThreadStackManager(implicit microVM: MicroVM) {
val futexManager = new FutexManager
/**
* The current Mu thread that is calling a native function via CCALL.
*/
var threadCallingNative: Option[InterpreterThread] = None
/**
* Create a new stack with function and args as the stack-bottom function and its arguments.
* <p>
......@@ -64,60 +73,62 @@ class ThreadStackManager(implicit microVM: MicroVM) {
thr
}
def joinAll() {
/**
* Execute one instruction in each currently executable thread.
*/
def roundRobin(): Boolean = {
var someRunning: Boolean = false
var someWaiting: Boolean = false
futexManager.futexWakeTimeout()
someRunning = false
someWaiting = false
val curThreads = threadRegistry.values.toList
for (thr2 <- curThreads) {
if (thr2.isRunning)
if (thr2.isFutexWaiting) {
someWaiting = thr2.isFutexWaiting || someWaiting
} else {
thr2.step()
someRunning = thr2.isRunning || someRunning
}
}
var continue: Boolean = false
do {
futexManager.futexWakeTimeout()
someRunning = false
someWaiting = false
val curThreads = threadRegistry.values.toList
for (thr2 <- curThreads) {
if (thr2.isRunning)
if (thr2.isFutexWaiting) {
someWaiting = thr2.isFutexWaiting || someWaiting
} else {
thr2.step()
someRunning = thr2.isRunning || someRunning
val shouldContinue = if (someRunning) {
true
} else {
if (someWaiting) {
futexManager.nextWakeup match {
case Some(nextWakeup) => {
val now = System.currentTimeMillis() * 1000000L
val sleep = nextWakeup - now
val sleepMillis = sleep / 1000000L
val sleepNanos = sleep % 1000000L
logger.debug("Waiting for futex. Now: %d, next wake up: %d, sleep: %d".format(now, nextWakeup, sleep))
Thread.sleep(sleepMillis, sleepNanos.toInt)
true
}
}
continue = if (someRunning) {
true
} else {
if (someWaiting) {
futexManager.nextWakeup match {
case Some(nextWakeup) => {
val now = System.currentTimeMillis() * 1000000L
val sleep = nextWakeup - now
val sleepMillis = sleep / 1000000L
val sleepNanos = sleep % 1000000L
logger.debug("Waiting for futex. Now: %d, next wake up: %d, sleep: %d".format(now, nextWakeup, sleep))
Thread.sleep(sleepMillis, sleepNanos.toInt)
true
}
case None => {
logger.error("No threads are running. No threads are waiting for futex with timer. This is a deadlock situation.")
false
}
case None => {
logger.error("No threads are running. No threads are waiting for futex with timer. This is a deadlock situation.")
false
}
} else {
false
}
} else {
false
}
} while (continue)
}
shouldContinue
}
def joinThread(thr: InterpreterThread) {
while (thr.isRunning) {
val curThreads = threadRegistry.values.toList
for (thr2 <- curThreads) {
thr2.step()
}
}
/**
* Execute until all threads stopped.
*/
def execute() {
var shouldContinue: Boolean = false
do {
shouldContinue = roundRobin()
} while (shouldContinue)
}
}
......@@ -11,13 +11,17 @@ import uvm.refimpl.itpr._
import uvm.refimpl.itpr.ValueBox
import uvm.refimpl.mem.TypeSizes
import uvm.refimpl.mem.TypeSizes.Word
import uvm.{Function => MFunc}
import uvm.{ Function => MFunc }
import uvm.types._
import uvm.types.{ Type => MType }
import uvm.utils.LazyPool
import uvm.utils.HexDump
import scala.collection.mutable.HashMap
import com.kenai.jffi.Closure
import com.kenai.jffi.ClosureManager
import com.kenai.jffi.CallingConvention
import uvm.refimpl.UvmRuntimeException
import uvm.refimpl.MicroVM
object NativeCallHelper {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
......@@ -26,9 +30,9 @@ object NativeCallHelper {
/**
* Helps calling native functions and supports callbacks from native. Based on JFFI.
*/
class NativeCallHelper {
class NativeCallHelper(implicit microVM: MicroVM) {
import NativeCallHelper._
/** A mapping of Mu types to JFFI types. Cached for struct types. */
val jffiTypePool: LazyPool[MType, JType] = LazyPool {
case TypeVoid() => JType.VOID
......@@ -56,7 +60,7 @@ class NativeCallHelper {
new JFunction(funcAddr, jRetTy, jParamTypes: _*)
}
}
/**
* A dynamically-exposed Mu function. A Mu function may be exposed many times. Each DynExpFunc corresponds to one
* such callable instance.
......@@ -64,18 +68,68 @@ class NativeCallHelper {
* A ".expose" definition will permanently create an instance.
* <p>
* The "@uvm.native.expose" instruction will also create one such instance. Such instances can be removed later by
* "@uvm.native.unexpose". The equivalent API calls do the same.
* "@uvm.native.unexpose". The equivalent API calls do the same.
*/
class DynExpFunc(val muFunc: MFunc, val cookie: Long, val closure: MuCallbackClosure, val closureHandle: Closure.Handle)
/**
* Map each address of closure handle to the DynExpFunc record so that the closure handle can be disposed.
*/
class DynExpFunc(val muFunc: MFunc, val closureHandle: Closure.Handle) {
val addr = closureHandle.getAddress()
}
val exposedFuncs = new HashMap[Word, DynExpFunc]()
def exposeFunc(muFunc: MFunc, cookie: Word): Word = {
???
/** Call a native (C) function. */
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox], retBox: ValueBox): Unit = {
val jFunc = jffiFuncPool((sig, func))
val hib = new HeapInvocationBuffer(jFunc)
for ((mty, vb) <- (sig.paramTy zip args)) {
putArg(hib, mty, vb)
}
val inv = Invoker.getInstance
sig.retTy match {
case TypeVoid() => {
inv.invokeLong(jFunc, hib)
}
case TypeInt(8) => {
val rv = inv.invokeInt(jFunc, hib).toByte
retBox.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(rv), 8)
}
case TypeInt(16) => {
val rv = inv.invokeInt(jFunc, hib).toShort
retBox.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(rv), 16)
}
case TypeInt(32) => {
val rv = inv.invokeInt(jFunc, hib)
retBox.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(rv), 32)
}
case TypeInt(64) => {
val rv = inv.invokeLong(jFunc, hib)
retBox.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(rv), 64)
}
case TypeFloat() => {
val rv = inv.invokeFloat(jFunc, hib)
retBox.asInstanceOf[BoxFloat].value = rv
}
case TypeDouble() => {
val rv = inv.invokeDouble(jFunc, hib)
retBox.asInstanceOf[BoxDouble].value = rv
}
case TypeStruct(flds) => {
val rv = inv.invokeStruct(jFunc, hib)
val buf = ByteBuffer.wrap(rv).order(ByteOrder.LITTLE_ENDIAN)
logger.debug("Hexdump:\n" + HexDump.dumpByteBuffer(buf))
getArgFromBuf(buf, 0, sig.retTy, retBox)
}
case _: AbstractPointerType => {
val rv = inv.invokeAddress(jFunc, hib)
retBox.asInstanceOf[BoxPointer].addr = rv
}
}
}
private def putArgToBuf(buf: ByteBuffer, off: Int, mty: MType, vb: ValueBox): Unit = {
mty match {
case TypeInt(8) => buf.put(off, vb.asInstanceOf[BoxInt].value.toByte)
......@@ -135,58 +189,40 @@ class NativeCallHelper {
case _: AbstractPointerType => vb.asInstanceOf[BoxPointer].addr = buf.getLong(off)
}
}
/** Call a native (C) function. */
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox], retBox: ValueBox): Unit = {
val jFunc = jffiFuncPool((sig, func))
val hib = new HeapInvocationBuffer(jFunc)
for ((mty, vb) <- (sig.paramTy zip args)) {
putArg(hib, mty, vb)
/**
* Expose a Mu function.
*
* @return the address of the exposed function (i.e. of the closure handle)
*/
def exposeFunc(muFunc: MFunc, cookie: Long): Word = {
val sig = muFunc.sig
val jParamTypes = sig.paramTy.map(jffiTypePool.apply)
val jRetTy = jffiTypePool(sig.retTy)
val clos = new MuCallbackClosure(muFunc, cookie)
val handle = NativeSupport.cloaureManager.newClosure(clos, jRetTy, jParamTypes.toArray, CallingConvention.DEFAULT)
val addr = handle.getAddress
val dynExpFunc = new DynExpFunc(muFunc, cookie, clos, handle)
exposedFuncs(addr) = dynExpFunc
addr
}
def unexposeFunc(addr: Word): Unit = {
val dynExpFunc = exposedFuncs.remove(addr).getOrElse {
throw new UvmRuntimeException("Attempt to unexpose function %d (0x%x) which has not been exposed.".format(addr, addr))
}
val inv = Invoker.getInstance
sig.retTy match {
case TypeVoid() => {
inv.invokeLong(jFunc, hib)
}
case TypeInt(8) => {
val rv = inv.invokeInt(jFunc, hib).toByte
retBox.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(rv), 8)
}
case TypeInt(16) => {
val rv = inv.invokeInt(jFunc, hib).toShort
retBox.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(rv), 16)
}
case TypeInt(32) => {
val rv = inv.invokeInt(jFunc, hib)
retBox.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(rv), 32)
}
case TypeInt(64) => {
val rv = inv.invokeLong(jFunc, hib)
retBox.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(rv), 64)
}
case TypeFloat() => {
val rv = inv.invokeFloat(jFunc, hib)
retBox.asInstanceOf[BoxFloat].value = rv
}
case TypeDouble() => {
val rv = inv.invokeDouble(jFunc, hib)
retBox.asInstanceOf[BoxDouble].value = rv
}
case TypeStruct(flds) => {
val rv = inv.invokeStruct(jFunc, hib)
val buf = ByteBuffer.wrap(rv).order(ByteOrder.LITTLE_ENDIAN)
logger.debug("Hexdump:\n" + HexDump.dumpByteBuffer(buf))
getArgFromBuf(buf, 0, sig.retTy, retBox)
}
case _: AbstractPointerType => {
val rv = inv.invokeAddress(jFunc, hib)
retBox.asInstanceOf[BoxPointer].addr = rv
}
dynExpFunc.closureHandle.dispose()
}
/** Handles calling back from C */
class MuCallbackClosure(val muFunc: MFunc, val cookie: Long) extends Closure {
def invoke(buf: Closure.Buffer): Unit = {
???
}
}
}
\ No newline at end of file
package uvm.refimpl.nat
import jnr.ffi.{ Runtime, Memory, Pointer }
import com.kenai.jffi.ClosureManager
/**
* Holder of JNR-specific resources.
......@@ -8,4 +9,5 @@ import jnr.ffi.{ Runtime, Memory, Pointer }
object NativeSupport {
val jnrRuntime = Runtime.getSystemRuntime
val theMemory = Pointer.wrap(jnrRuntime, 0L)
val cloaureManager = ClosureManager.getInstance()
}
\ No newline at end of file
......@@ -36,7 +36,7 @@ object FactorialFromRPython extends App {
val sta = ca.newStack(m, Seq())
val thr = ca.newThread(sta)
microVM.threadStackManager.joinAll() // run until all threads stop
microVM.execute() // run until all threads stop
ca.close()
}
\ No newline at end of file
......@@ -68,7 +68,7 @@ abstract class UvmBundleTesterBase extends FlatSpec with Matchers {
microVM.trapManager.trapHandler = new MockTrapHandler(handler)
val hStack = ca.newStack(func, args)
val hThread = ca.newThread(hStack)
microVM.threadStackManager.joinAll()
microVM.execute()
}
implicit class MagicalBox(vb: ValueBox) {
......
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