Commit 18eb5b94 authored by Kunshan Wang's avatar Kunshan Wang

Work-around errno problem.

parent d5bf3607
......@@ -531,7 +531,7 @@ trait InstructionExecutor extends InterpreterActions with CommInstExecutor {
val argBoxes = argList.map(boxOf)
val shouldIncrementPC = curStack.callNative(sig, addr, argBoxes)
val shouldIncrementPC = curStack.callNative(sig, addr, argBoxes, errnoBox)
if (shouldIncrementPC) {
continueNormally()
}
......
......@@ -54,6 +54,17 @@ class InterpreterThread(val id: MuInternalID, initialStack: InterpreterStack, in
}
}
/**
* This box holds the per-mu-thread errno. It's a Hack.
* <p>
* Every InterpreterThread holds an ErrnoBox.
* Before every CCALL, set errno to savedLastError; after every CCALL, set savedLastError to errno. In this way,
* native programs see a "continuum" where the errno is never observed to change inside the micro VM.
*/
class ErrnoBox {
var errno: Int = 0
}
/**
* The states of an interpreter thread.
*/
......@@ -78,6 +89,9 @@ trait InterpreterThreadState {
/** Thread-local object reference. */
val threadLocal = BoxRef(0L)
/** The per-thread errno. */
val errnoBox = new ErrnoBox
protected def curStack = stack.get
protected def top: InterpreterFrame = curStack.top
......
......@@ -210,11 +210,11 @@ class InterpreterStack(val id: MuInternalID, val stackMemory: StackMemory, stack
}
/** Mu calling a native function. */
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox]): Boolean = {
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox], errnoBox: ErrnoBox): Boolean = {
assert(top.isInstanceOf[MuFrame])
ensureNSK()
pushNativeFrame(sig, func)
val result = maybeNSK.get.callNative(sig, func, args)
val result = maybeNSK.get.callNative(sig, func, args, errnoBox)
handleNativeCallResult(result)
}
......
......@@ -225,11 +225,20 @@ class NativeCallHelper {
* to Mu (to JVM).
*/
val currentNativeStackKeeper = new ThreadLocal[NativeStackKeeper]()
/**
* The saved errno.
* <p>
* Before every CCALL, set errno to savedLastError; after every CCALL, set savedLastError to errno. In this way,
* native programs see a "continuum" where the errno is never observed to change inside the micro VM.
*/
var savedLastError: Int = 0
/**
* Call a native function. Must be called by a NativeStackKeeper.Slave thread.
*/
def callNative(nsk: NativeStackKeeper, sig: FuncSig, func: Word, args: Seq[ValueBox]): Option[ValueBox] = {
def callNative(nsk: NativeStackKeeper, sig: FuncSig, func: Word, args: Seq[ValueBox],
errnoBox: ErrnoBox): Option[ValueBox] = {
assert(Thread.currentThread() == nsk.slaveThread)
val jFunc = jffiFuncPool((sig, func))
......@@ -244,53 +253,76 @@ class NativeCallHelper {
assert(currentNativeStackKeeper.get() == nsk)
val inv = Invoker.getInstance
logger.debug("Before CCALL, saved errno = %d".format(errnoBox.errno))
val maybeRvb = sig.retTys match {
case Seq() => {
NativeSupport.jffiLastError.set(errnoBox.errno)
inv.invokeLong(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
None
}
case Seq(t) => {
val b = t match {
case TypeInt(8) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeInt(jFunc, hib).toByte
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxInt(OpHelper.trunc(BigInt(rv), 8))
}
case TypeInt(16) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeInt(jFunc, hib).toShort
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxInt(OpHelper.trunc(BigInt(rv), 16))
}
case TypeInt(32) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeInt(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxInt(OpHelper.trunc(BigInt(rv), 32))
}
case TypeInt(64) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeLong(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxInt(OpHelper.trunc(BigInt(rv), 64))
}
case TypeFloat() => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeFloat(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxFloat(rv)
}
case TypeDouble() => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeDouble(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxDouble(rv)
}
case TypeStruct(flds) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeStruct(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
val buf = ByteBuffer.wrap(rv).order(ByteOrder.LITTLE_ENDIAN)
logger.debug("Hexdump:\n" + HexDump.dumpByteBuffer(buf))
val ptr = Pointer.wrap(NativeSupport.jnrRuntime, buf)
makeBoxFromPtr(ptr, 0, t)
}
case _: AbstractPointerType => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeAddress(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxPointer(rv)
}
}
Some(b)
}
}
logger.debug("After CCALL, saved errno = %d".format(errnoBox.errno))
currentNativeStackKeeper.remove()
maybeRvb
......
package uvm.refimpl.nat
import com.kenai.jffi.ClosureManager
import jnr.ffi.provider.jffi.NativeRuntime
import com.kenai.jffi.LastError
import com.kenai.jffi.MemoryIO
import jnr.ffi.Pointer
import jnr.ffi.provider.jffi.NativeRuntime
/**
* Holder of JNR-specific resources.
......@@ -17,6 +18,7 @@ object NativeSupport {
// This is from JFFI, not JNR-FFI.
val jffiClosureManager = ClosureManager.getInstance()
val jffiLastError = LastError.getInstance()
// Explicit memory management
......
......@@ -11,6 +11,7 @@ import uvm.{ Function => MFunc }
import uvm.refimpl.UvmRuntimeException
import uvm.refimpl.Word
import uvm.refimpl.itpr.ValueBox
import uvm.refimpl.itpr.ErrnoBox
object PoorManAgent {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
......@@ -70,7 +71,7 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo
abstract class ToSlave
object ToSlave {
/** Mu wants to call a native function. */
case class CallNative(sig: FuncSig, func: Word, args: Seq[ValueBox]) extends ToSlave
case class CallNative(sig: FuncSig, func: Word, args: Seq[ValueBox], errnoBox: ErrnoBox) extends ToSlave
/** Mu wants to return to a native function. maybeRvb holds the return value. */
case class ReturnToNative(maybeRvb: Option[ValueBox]) extends ToSlave
/** Mu wants the slave to stop. */
......@@ -92,8 +93,8 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo
return
}
received match {
case ToSlave.CallNative(sig, func, args) => {
val maybeRvb = nativeCallHelper.callNative(master, sig, func, args)
case ToSlave.CallNative(sig, func, args, errnoBox) => {
val maybeRvb = nativeCallHelper.callNative(master, sig, func, args, errnoBox)
master.send(NativeCallResult.ReturnToMu(maybeRvb))
receiving()
}
......@@ -129,8 +130,8 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo
throw e
}
received match {
case ToSlave.CallNative(sig, func, args) => {
val maybeRvb = nativeCallHelper.callNative(master, sig, func, args)
case ToSlave.CallNative(sig, func, args, errnoBox) => {
val maybeRvb = nativeCallHelper.callNative(master, sig, func, args, errnoBox)
master.send(NativeCallResult.ReturnToMu(maybeRvb))
receiving()
}
......@@ -162,8 +163,8 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo
val slaveThread = new Thread(slave)
slaveThread.start()
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox]): NativeCallResult = {
slave.send(ToSlave.CallNative(sig, func, args))
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox], errnoBox: ErrnoBox): NativeCallResult = {
slave.send(ToSlave.CallNative(sig, func, args, errnoBox))
val result = receive()
result
}
......
package uvm.refimpl.itpr
import org.scalatest._
import java.io.FileReader
import uvm._
import uvm.types._
import uvm.ssavariables._
import uvm.refimpl._
import uvm.refimpl.itpr._
import MemoryOrder._
import AtomicRMWOptr._
import uvm.refimpl.Word
import ch.qos.logback.classic.Level._
import uvm.refimpl.UvmBundleTesterBase
import com.kenai.jffi.Library
import jnr.posix.POSIXFactory
import uvm.refimpl.nat.NativeLibraryTestHelper
class UvmInterpreterNativeErrnoTest extends UvmBundleTesterBase {
setLogLevels(
ROOT_LOGGER_NAME -> INFO,
"uvm.refimpl.itpr" -> DEBUG,
"uvm.refimpl.nat" -> DEBUG
)
NativeLibraryTestHelper.loadTestLibraryToMicroVM(microVM, "simplecfuncs")
preloadBundles("tests/uvm-refimpl-test/primitives.uir", "tests/uvm-refimpl-test/native-errno-test.uir")
"The CCALL instruction" should "save errno to thread's errno box" in {
val ctx = microVM.newContext()
val func = ctx.handleFromFunc("@errno_test")
val a0 = ctx.handleFromInt(42, 32)
testFunc(ctx, func, Seq(a0)) { (ctx, th, st, wp) =>
val Seq(rv: MuIntValue) = ctx.dumpKeepalives(st, 0)
ctx.handleToSInt(rv) shouldEqual 42
returnFromTrap(st)
}
ctx.closeContext()
}
}
\ No newline at end of file
package uvm.refimpl.nat
import com.kenai.jffi.Library
import uvm.refimpl.MicroVM
object NativeLibraryTestHelper {
def loadTestLibrary(name: String): Library = {
def getLibPath(name: String): String = {
val relPath = s"tests/c-snippets/${name}.so"
if (!new java.io.File(relPath).isFile()) {
throw new RuntimeException(s"Need to compile the ${name}.so library. cd into tests/c-snippets and invoke 'make'.")
}
relPath
}
def loadTestLibrary(name: String): Library = {
val relPath = getLibPath(name)
Library.openLibrary(relPath, Library.NOW)
}
def loadTestLibraryToMicroVM(microVM: MicroVM, name: String): Unit = {
val relPath = getLibPath(name)
microVM.nativeLibraryHolder.loadLibrary(relPath)
}
}
\ No newline at end of file
......@@ -52,7 +52,7 @@ class NativeStackKeeperTest extends UvmTestBase with ExtraMatchers {
val box1 = BoxInt(3)
val box2 = BoxInt(4)
val result = nsk.callNative(sig, addr, Seq(box1, box2))
val result = nsk.callNative(sig, addr, Seq(box1, box2), new ErrnoBox)
result shouldBeA[NativeCallResult.ReturnToMu] { its =>
its.maybeRvb shouldBe Some(BoxInt(7))
......@@ -84,7 +84,7 @@ class NativeStackKeeperTest extends UvmTestBase with ExtraMatchers {
val b1 = BoxDouble(3.0)
val b2 = BoxPointer(mockClosAddr)
val r1 = nsk.callNative(sig, addr, Seq(b1, b2))
val r1 = nsk.callNative(sig, addr, Seq(b1, b2), new ErrnoBox)
println("Hello. I received r1")
......@@ -145,7 +145,7 @@ class NativeStackKeeperTest extends UvmTestBase with ExtraMatchers {
val b2 = BoxPointer(closHandle.getAddress)
val br = BoxDouble(-1.0)
val result = nsk.callNative(sig, addr, Seq(b1, b2))
val result = nsk.callNative(sig, addr, Seq(b1, b2), new ErrnoBox)
closHandle.dispose()
......
#include<stdio.h>
#include<errno.h>
int hello_world(int n) {
printf("Hello world from C!\n");
return n + 1;
}
void set_errno(int n) {
printf("Hi! I am about to set errno to %d\n", n);
errno = n;
}
int get_errno() {
return errno;
}
// require "primitives.uir"
.funcsig @set_errno.sig = (@i32) -> ()
.funcsig @get_errno.sig = () -> (@i32)
.typedef @set_errno.fp = ufuncptr<@set_errno.sig>
.typedef @get_errno.fp = ufuncptr<@get_errno.sig>
.const @set_errno <@set_errno.fp> = EXTERN "set_errno"
.const @get_errno <@get_errno.fp> = EXTERN "get_errno"
.funcsig @errno_test.sig = (@i32) -> ()
.funcdef @errno_test VERSION %1 <@errno_test.sig> {
%entry(<@i32> %p0):
CCALL #DEFAULT <@set_errno.fp @set_errno.sig> @set_errno (%p0)
%rv = CCALL #DEFAULT <@get_errno.fp @get_errno.sig> @get_errno ()
[%trap] TRAP <> KEEPALIVE (%rv)
COMMINST @uvm.thread_exit
}
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