From 34aaaa4ccb12bdb5808ec72e4a7552f5885b9036 Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Mon, 10 Apr 2023 14:12:50 +0200 Subject: [PATCH] Routing ideas --- phasmplatform/common/router.py | 8 +++ phasmplatform/worker/__main__.py | 17 +++++- phasmplatform/worker/runner.py | 17 +++++- phasmplatform/worker/runners/__init__.py | 0 phasmplatform/worker/runners/base.py | 33 ++++++++++ phasmplatform/worker/runners/wasmtime.py | 77 ++++++++++++++++++++++++ requirements.txt | 1 + 7 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 phasmplatform/common/router.py create mode 100644 phasmplatform/worker/runners/__init__.py create mode 100644 phasmplatform/worker/runners/base.py create mode 100644 phasmplatform/worker/runners/wasmtime.py diff --git a/phasmplatform/common/router.py b/phasmplatform/common/router.py new file mode 100644 index 0000000..48e427f --- /dev/null +++ b/phasmplatform/common/router.py @@ -0,0 +1,8 @@ +class BaseRouter: + def post_message(self, namespace: bytes, topic: bytes, kind: bytes, body: bytes) -> None: + raise NotImplementedError + + +class StdOutRouter(BaseRouter): + def post_message(self, namespace: bytes, topic: bytes, kind: bytes, body: bytes) -> None: + print(f'{namespace.decode()}: {topic.decode()}: {kind.decode()}: {body.decode()}') diff --git a/phasmplatform/worker/__main__.py b/phasmplatform/worker/__main__.py index f036886..9a9e7ff 100644 --- a/phasmplatform/worker/__main__.py +++ b/phasmplatform/worker/__main__.py @@ -1,8 +1,10 @@ import sys from phasmplatform.common.config import from_toml +from phasmplatform.common.router import StdOutRouter -from .runner import run_once +from .runners.base import BaseRunner +from .runners.wasmtime import WasmTimeRunner def main() -> int: @@ -11,8 +13,19 @@ def main() -> int: del config + stdout_router = StdOutRouter() + + foo: BaseRunner + with open('/home/johan/projects/idea/phasm/examples/crc32.wasm', 'rb') as fil: - run_once(fil.read()) + foo = WasmTimeRunner(stdout_router, fil.read()) + + namespace = b'test-namespace' + topic = b'test-topic' + kind = b'test-kind' + body = b'test-body' + + foo.handle_message(namespace, topic, kind, body) return 0 diff --git a/phasmplatform/worker/runner.py b/phasmplatform/worker/runner.py index 7e0c921..402f66a 100644 --- a/phasmplatform/worker/runner.py +++ b/phasmplatform/worker/runner.py @@ -3,11 +3,22 @@ import wasm3 # type: ignore from phasmplatform.common.exceptions import PhashPlatformNonIntMainReturnError -def run_once(wasm_bin: bytes) -> int: +def post_message(namespace: int, topic: int, kind: int, body: int) -> None: + print('namespace', namespace) + print('topic', topic) + print('kind', kind) + print('body', body) + + +def run_once_wasm3(wasm_bin: bytes) -> int: env = wasm3.Environment() rtime = env.new_runtime(1024 * 1024) - rtime.load(env.parse_module(wasm_bin)) - result = rtime.find_function('main')() + + mod = env.parse_module(wasm_bin) + mod.link_function('imports', 'post_message', post_message) + + rtime.load(mod) + result = rtime.find_function('handle_message')() if result is None: return 0 diff --git a/phasmplatform/worker/runners/__init__.py b/phasmplatform/worker/runners/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/phasmplatform/worker/runners/base.py b/phasmplatform/worker/runners/base.py new file mode 100644 index 0000000..2eaace4 --- /dev/null +++ b/phasmplatform/worker/runners/base.py @@ -0,0 +1,33 @@ +from phasmplatform.common.router import BaseRouter + + +class BaseRunner: + __slots__ = ('router', ) + + router: BaseRouter + + def __init__(self, router: BaseRouter) -> None: + self.router = router + + def alloc_bytes(self, data: bytes) -> int: + """ + Calls upon stdlib.types.__alloc_bytes__ to allocate a bytes object + """ + raise NotImplementedError + + def read_bytes(self, ptr: int) -> bytes: + """ + Reads a byte object allocated by stdlib.types.__alloc_bytes__ + """ + raise NotImplementedError + + def handle_message(self, namespace: bytes, topic: bytes, kind: bytes, body: bytes) -> None: + raise NotImplementedError + + def post_message(self, namespace_ptr: int, topic_ptr: int, kind_ptr: int, body_ptr: int) -> None: + 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) diff --git a/phasmplatform/worker/runners/wasmtime.py b/phasmplatform/worker/runners/wasmtime.py new file mode 100644 index 0000000..e90605f --- /dev/null +++ b/phasmplatform/worker/runners/wasmtime.py @@ -0,0 +1,77 @@ +import ctypes +import struct + +import wasmtime + +from phasmplatform.common.router import BaseRouter +from .base import BaseRunner + + +class WasmTimeRunner(BaseRunner): + __slots__ = ('store', 'module', 'instance', 'exports') + + def __init__(self, router: BaseRouter, wasm_bin: bytes) -> None: + super().__init__(router) + + self.store = wasmtime.Store() + + post_message_bind = wasmtime.Func(self.store, wasmtime.FuncType([ + wasmtime.ValType.i32(), + wasmtime.ValType.i32(), + wasmtime.ValType.i32(), + wasmtime.ValType.i32(), + ], []), self.post_message) + + self.module = wasmtime.Module(self.store.engine, wasm_bin) + self.instance = wasmtime.Instance(self.store, self.module, [ + post_message_bind, + ]) + + self.exports = self.instance.exports(self.store) + + def alloc_bytes(self, data: bytes) -> int: + alloc_bytes = self.exports['stdlib.types.__alloc_bytes__'] + assert isinstance(alloc_bytes, wasmtime.Func) + ptr = alloc_bytes(self.store, len(data)) + assert isinstance(ptr, int) # type hint + + memory = self.exports['memory'] + assert isinstance(memory, wasmtime.Memory) # type hint + + data_ptr = memory.data_ptr(self.store) + data_len = memory.data_len(self.store) + + idx = ptr + 8 # This is the header from alloc plus the header from __alloc_bytes__ + for byt in data: + assert idx < data_len + data_ptr[idx] = ctypes.c_ubyte(byt) + idx += 1 + + return ptr + + def read_bytes(self, ptr: int) -> bytes: + memory = self.exports['memory'] + assert isinstance(memory, wasmtime.Memory) # type hint + + data_ptr = memory.data_ptr(self.store) + data_len = memory.data_len(self.store) + + raw = ctypes.string_at(data_ptr, data_len) + + ptr = ptr + 4 # This is the header from alloc + + print('raw[ptr:ptr + 4]', raw[ptr:ptr + 4]) + length = struct.unpack(' 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) diff --git a/requirements.txt b/requirements.txt index f572e23..ce77090 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ pywasm3==0.5.0 redis==4.5.4 tomli==2.0.1 ; python_version < '3.11' +wasmtime==7.0.0