Some running already, on_module_loaded

This commit is contained in:
Johan B.W. de Vries 2023-04-11 10:26:56 +02:00
parent 8c5a2893d6
commit 1ff21c7f29
4 changed files with 115 additions and 84 deletions

View File

@ -1,6 +1,7 @@
from typing import Any, List from typing import Any, Callable, List
from .value import Value
from .valuetype import ValueType from .valuetype import ValueType
@ -32,3 +33,32 @@ class Method:
self.name = name self.name = name
self.args = args self.args = args
self.return_type = return_type self.return_type = return_type
class MethodCallError:
pass
class MethodNotFoundError(MethodCallError):
pass
class MethodCall:
__slots__ = ('method', 'args', 'on_success', 'on_error', )
method: Method
args: List[Value]
on_success: Callable[[Value], None]
on_error: Callable[[MethodCallError], None]
def __init__(
self,
method: Method,
args: List[Value],
on_success: Callable[[Value], None],
on_error: Callable[[MethodCallError], None],
) -> None:
self.method = method
self.args = args
self.on_success = on_success
self.on_error = on_error

View File

@ -1,26 +1,25 @@
import sys import sys
import threading
import time
from queue import Empty, Queue
from phasmplatform.common import valuetype from phasmplatform.common import valuetype
from phasmplatform.common.config import from_toml from phasmplatform.common.config import from_toml
from phasmplatform.common.method import Method, MethodArgument from phasmplatform.common.method import Method, MethodCall
from phasmplatform.common.router import StdOutRouter from phasmplatform.common.router import StdOutRouter
from phasmplatform.common.value import Value
from .runners.base import RunnerInterface from .runners.base import RunnerInterface
from .runners.wasmtime import WasmTimeRunner from .runners.wasmtime import WasmTimeRunner
def somefunc(runner: RunnerInterface) -> None: def runner_thread(runner: RunnerInterface, queue: Queue[MethodCall]) -> None:
inp = Value(valuetype.bytes, b'Hello, world!') while True:
print('inp', inp) try:
call = queue.get(block=True, timeout=1)
except Empty:
break
def on_respond(out: Value) -> None: runner.do_call(call)
print('out', out)
assert out == inp
echo = Method('echo', [MethodArgument('msg', valuetype.bytes)], valuetype.bytes)
runner.do_call(echo, [inp], on_respond)
def main() -> int: def main() -> int:
@ -31,17 +30,32 @@ def main() -> int:
stdout_router = StdOutRouter() stdout_router = StdOutRouter()
with open('/home/johan/projects/idea/phasm/examples/platform.wasm', 'rb') as fil: with open('/home/johan/projects/idea/phasm/examples/echoclient.wasm', 'rb') as fil:
foo = WasmTimeRunner(stdout_router, fil.read()) echo_client = WasmTimeRunner(stdout_router, fil.read())
# namespace = b'test-namespace' with open('/home/johan/projects/idea/phasm/examples/echoserver.wasm', 'rb') as fil:
# topic = b'test-topic' echo_server = WasmTimeRunner(stdout_router, fil.read())
# kind = b'test-kind'
# body = b'test-body'
# foo.handle_message(namespace, topic, kind, body) on_module_loaded = MethodCall(
Method('on_module_loaded', [], valuetype.none),
[],
lambda x: None,
lambda x: None, # TODO: Check for MethodNotFoundError, otherwise report it
)
somefunc(foo) echo_client_queue: Queue[MethodCall] = Queue()
echo_client_queue.put(on_module_loaded)
echo_server_queue: Queue[MethodCall] = Queue()
echo_server_queue.put(on_module_loaded)
echo_client_thread = threading.Thread(target=runner_thread, args=(echo_client, echo_client_queue))
echo_server_thread = threading.Thread(target=runner_thread, args=(echo_server, echo_server_queue))
echo_client_thread.start()
echo_server_thread.start()
time.sleep(3)
return 0 return 0

View File

@ -1,14 +1,16 @@
from typing import Callable, List, TextIO from typing import TextIO, Union
from phasmplatform.common import valuetype
from phasmplatform.common.router import BaseRouter from phasmplatform.common.router import BaseRouter
from phasmplatform.common.method import Method from phasmplatform.common.method import MethodCall
from phasmplatform.common.value import Value from phasmplatform.common.value import Value
from phasmplatform.common.valuetype import ValueType
class RunnerInterface: class RunnerInterface:
__slots__ = ('router', ) __slots__ = ('router', )
def do_call(self, method: Method, args: List[Value], on_result: Callable[[Value], None]) -> None: def do_call(self, call: MethodCall) -> None:
raise NotImplementedError raise NotImplementedError
@ -32,16 +34,26 @@ class BaseRunner(RunnerInterface):
""" """
raise NotImplementedError raise NotImplementedError
def handle_message(self, namespace: bytes, topic: bytes, kind: bytes, body: bytes) -> None: def value_to_wasm(self, val: Value) -> Union[None, int, float]:
raise NotImplementedError if val.value_type is valuetype.bytes:
assert isinstance(val.data, bytes) # type hint
return self.alloc_bytes(val.data)
def post_message(self, namespace_ptr: int, topic_ptr: int, kind_ptr: int, body_ptr: int) -> None: raise NotImplementedError(val)
namespace = self.read_bytes(namespace_ptr)
topic = self.read_bytes(topic_ptr)
kind = self.read_bytes(kind_ptr)
body = self.read_bytes(body_ptr)
self.router.post_message(namespace, topic, kind, body) def value_from_wasm(self, value_type: ValueType, val: Union[None, int, float]) -> Value:
if value_type is valuetype.none:
assert val is None # type hint
return Value(value_type, None)
if value_type is valuetype.bytes:
assert isinstance(val, int) # type hint
return Value(value_type, self.read_bytes(val))
raise NotImplementedError(value_type, val)
def log_bytes(self, msg_ptr: int) -> None:
print('LOG: ' + self.read_bytes(msg_ptr).decode())
def dump_memory(textio: TextIO, mem: bytes) -> None: def dump_memory(textio: TextIO, mem: bytes) -> None:

View File

@ -1,15 +1,12 @@
from typing import Callable, List, Union from typing import List
import ctypes import ctypes
import struct import struct
import wasmtime import wasmtime
from phasmplatform.common import valuetype from phasmplatform.common.method import MethodCall, MethodNotFoundError
from phasmplatform.common.method import Method
from phasmplatform.common.router import BaseRouter from phasmplatform.common.router import BaseRouter
from phasmplatform.common.value import Value
from phasmplatform.common.valuetype import ValueType
from .base import BaseRunner from .base import BaseRunner
@ -21,17 +18,25 @@ class WasmTimeRunner(BaseRunner):
self.store = wasmtime.Store() self.store = wasmtime.Store()
post_message_bind = wasmtime.Func(self.store, wasmtime.FuncType([ possible_imports = {
wasmtime.ValType.i32(), 'prelude': {
wasmtime.ValType.i32(), 'log_bytes': wasmtime.Func(self.store, wasmtime.FuncType([wasmtime.ValType.i32()], []), self.log_bytes),
wasmtime.ValType.i32(), }
wasmtime.ValType.i32(), }
], []), self.post_message)
self.module = wasmtime.Module(self.store.engine, wasm_bin) self.module = wasmtime.Module(self.store.engine, wasm_bin)
self.instance = wasmtime.Instance(self.store, self.module, [
post_message_bind, imports: List[wasmtime.Func] = []
]) for imprt in self.module.imports:
if imprt.module not in possible_imports:
raise Exception('Service not found')
if imprt.name not in possible_imports[imprt.module]:
raise Exception('Method not found in service')
imports.append(possible_imports[imprt.module][imprt.name])
self.instance = wasmtime.Instance(self.store, self.module, imports)
self.exports = self.instance.exports(self.store) self.exports = self.instance.exports(self.store)
@ -69,46 +74,16 @@ class WasmTimeRunner(BaseRunner):
return raw[ptr + 4:ptr + 4 + length] return raw[ptr + 4:ptr + 4 + length]
def value_to_wasm(self, val: Value) -> Union[None, int, float]: def do_call(self, call: MethodCall) -> None:
if val.value_type is valuetype.bytes: try:
assert isinstance(val.data, bytes) # type hint wasm_method = self.exports[call.method.name]
return self.alloc_bytes(val.data) except KeyError:
call.on_error(MethodNotFoundError())
return
raise NotImplementedError(val)
def value_from_wasm(self, value_type: ValueType, val: Union[None, int, float]) -> Value:
if value_type is valuetype.bytes:
assert isinstance(val, int) # typ hint
return Value(valuetype.bytes, self.read_bytes(val))
raise NotImplementedError(value_type, val)
def do_call(self, method: Method, args: List[Value], on_result: Callable[[Value], None]) -> None:
wasm_method = self.exports[method.name]
assert isinstance(wasm_method, wasmtime.Func) assert isinstance(wasm_method, wasmtime.Func)
act_args = [self.value_to_wasm(x) for x in args] act_args = [self.value_to_wasm(x) for x in call.args]
result = wasm_method(self.store, *act_args) result = wasm_method(self.store, *act_args)
assert result is None or isinstance(result, (int, float, )) # type hint assert result is None or isinstance(result, (int, float, )) # type hint
on_result(self.value_from_wasm(method.return_type, result)) call.on_success(self.value_from_wasm(call.method.return_type, result))
# callback(UntypedValue(result)) # TODO: This returns a bytes pointer, but we can't detect that in advance
# def do_call(self, method_name: str, args: List[BaseValue[Any]], callback: Callable[[BaseValue[Any]], None]) -> None:
# method = self.exports[method_name]
# assert isinstance(method, wasmtime.Func)
# act_args = [self.convert_value(x) for x in args]
# result = method(self.store, *act_args)
# callback(UntypedValue(result)) # TODO: This returns a bytes pointer, but we can't detect that in advance
def handle_message(self, namespace: bytes, topic: bytes, kind: bytes, body: bytes) -> None:
namespace_ptr = self.alloc_bytes(namespace)
topic_ptr = self.alloc_bytes(topic)
kind_ptr = self.alloc_bytes(kind)
body_ptr = self.alloc_bytes(body)
handle_message = self.exports['handle_message']
assert isinstance(handle_message, wasmtime.Func)
handle_message(self.store, namespace_ptr, topic_ptr, kind_ptr, body_ptr)