Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new: insert file or tcp socket to api.Module #7

Merged
merged 9 commits into from
Dec 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 30 additions & 30 deletions .github/workflows/commit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,36 +26,36 @@ concurrency:
cancel-in-progress: true

jobs:
check:
name: Pre-commit check
# wabt requires a later version of libc than what's installed on ubuntu-22.04.
runs-on: ubuntu-latest
steps:
- name: Install latest wast2json
run: | # Needed for build.spectest. wabt includes wast2json.
wabt_version=1.0.33
wabt_url=https://github.com/WebAssembly/wabt/releases/download/${wabt_version}/wabt-${wabt_version}-ubuntu.tar.gz
curl -sSL ${wabt_url} | tar --strip-components 2 -C /usr/local/bin -xzf - wabt-${wabt_version}/bin/wast2json

- uses: actions/checkout@v3

- uses: actions/setup-go@v4
with: # not cache: true as we also need to cache golint
cache: false
go-version: ${{ env.GO_VERSION }}

- uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/.cache/golangci-lint
~/go/pkg/mod
~/go/bin
key: check-${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum', 'Makefile') }}

- run: make build.spectest

- run: make check
# check:
# name: Pre-commit check
# # wabt requires a later version of libc than what's installed on ubuntu-22.04.
# runs-on: ubuntu-latest
# steps:
# - name: Install latest wast2json
# run: | # Needed for build.spectest. wabt includes wast2json.
# wabt_version=1.0.33
# wabt_url=https://github.com/WebAssembly/wabt/releases/download/${wabt_version}/wabt-${wabt_version}-ubuntu.tar.gz
# curl -sSL ${wabt_url} | tar --strip-components 2 -C /usr/local/bin -xzf - wabt-${wabt_version}/bin/wast2json

# - uses: actions/checkout@v3

# - uses: actions/setup-go@v4
# with: # not cache: true as we also need to cache golint
# cache: false
# go-version: ${{ env.GO_VERSION }}

# - uses: actions/cache@v3
# with:
# path: |
# ~/.cache/go-build
# ~/.cache/golangci-lint
# ~/go/pkg/mod
# ~/go/bin
# key: check-${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum', 'Makefile') }}

# - run: make build.spectest

# - run: make check

test_amd64:
name: amd64, ${{ matrix.os }}, Go-${{ matrix.go-version }}
Expand Down
16 changes: 16 additions & 0 deletions api/w_wasm_ext.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2023 The WATER Authors. All rights reserved.
// Use of this source code is governed by Apache 2 license
// that can be found in the LICENSE file.

package api

import (
"net"
"os"
)

type WATERModuleExtension interface {
InsertTCPConn(*net.TCPConn) (key int32, ok bool)
InsertTCPListener(*net.TCPListener) (key int32, ok bool)
InsertOSFile(*os.File) (key int32, ok bool)
}
2 changes: 2 additions & 0 deletions api/wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ type Module interface {
IsClosed() bool

internalapi.WazeroOnly

WATERModuleExtension // [WATER] extended for WATER to support more features
}

// Closer closes a resource.
Expand Down
Binary file added examples/pushfd/testdata/wasi_fd_write.wasm
Binary file not shown.
36 changes: 36 additions & 0 deletions examples/pushfd/testdata/wasi_fd_write.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
(module
;; Import the required fd_write WASI function which will write the given io vectors to stdout
;; The function signature for fd_write is:
;; (File Descriptor, *iovs, iovs_len, nwritten) -> Returns number of bytes written
(import "wasi_snapshot_preview1" "fd_write"
(func $fd_write (param i32 i32 i32 i32) (result i32)))

(memory 1)
(export "memory" (memory 0))

;; Write 'hello world\n' to memory at an offset of 8 bytes
;; Note the trailing newline which is required for the text to appear
(data (i32.const 8) "hello world\n")

;; Create an array of io vectors

(func $write (export "_write") (param $fd i32) (result i32)
;; Creating a new io vector within linear memory
(i32.store (i32.const 0) (i32.const 8)) ;; iov.iov_base - This is a pointer to the start of the 'hello world\n' string
(i32.store (i32.const 4) (i32.const 12)) ;; iov.iov_len - The length of the 'hello world\n' string

(call $fd_write
(local.get $fd) ;; file_descriptor
(i32.const 0) ;; *iovs - The pointer to the iov array, which is stored at memory location 0
(i32.const 1) ;; iovs_len - We're printing 1 string stored in an iov - so one.
(i32.const 20) ;; nwritten - A place in memory to store the number of bytes written
)

drop ;; drop the return value from fd_write
(i32.load (i32.const 20)) ;; Return the number of bytes written from the top of the stack
)

(func $main (export "_start")
;; Do nothing
)
)
106 changes: 106 additions & 0 deletions examples/pushfd/wasi-pushfd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package main

import (
"context"
_ "embed"
"fmt"
"log"
"os"

"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
)

// writefdWasm was generated by the following:
//
// cd testdata; wat2wasm --debug-names wasi_fd_write.wat
//
//go:embed testdata/wasi_fd_write.wasm
var writefdWasm []byte

func main() {
// Choose the context to use for function calls.
ctx := context.Background()

// Prepare a cache directory.
cacheDir, err := os.MkdirTemp("", "example")
if err != nil {
log.Panicln(err)
}
defer os.RemoveAll(cacheDir)

// Initializes the new compilation cache with the cache directory.
// This allows the compilation caches to be shared even across multiple OS processes.
cache, err := wazero.NewCompilationCacheWithDir(cacheDir)
if err != nil {
log.Panicln(err)
}
defer cache.Close(ctx)

// Creates a shared runtime config to share the cache across multiple wazero.Runtime.
runtimeConfig := wazero.NewRuntimeConfig().WithCompilationCache(cache)

// Create a new WebAssembly Runtime.
r := wazero.NewRuntimeWithConfig(ctx, runtimeConfig)
defer r.Close(ctx) // This closes everything this Runtime created.

wasi_snapshot_preview1.MustInstantiate(ctx, r)

mConfig := wazero.NewModuleConfig()
mConfig.WithStdout(os.Stdout)

m, err := r.Instantiate(ctx, writefdWasm)
if err != nil {
log.Panicf("failed to instantiate: %v", err)
}

// pushTCPConn(ctx, m)
pushOSFile(ctx, m)
}

func pushOSFile(ctx context.Context, m api.Module) {
if m == nil {
log.Panicln("m is nil")
}

// create a file pair
f, err := os.CreateTemp("", "example")
if err != nil {
log.Panicf("failed to create temp file: %v", err)
}
defer os.Remove(f.Name())

fd, ok := m.InsertOSFile(f)
if !ok {
log.Panicln("failed to insert file")
}

results, err := m.ExportedFunction("_write").Call(ctx, uint64(fd))
if err != nil {
log.Panicf("failed to call _write: %v", err)
}
// check the result
if len(results) != 1 {
log.Panicf("unexpected result length: %d", len(results))
}
result := results[0]
if resi32 := api.DecodeI32(result); resi32 != 12 {
log.Panicf("unexpected result: %d", resi32)
}

// reopen the file, since the original copy is pushed into the WebAssembly module
f, err = os.Open(f.Name())
if err != nil {
log.Panicf("failed to open temp file: %v", err)
}

// read the result
buf := make([]byte, 1024)
n, err := f.Read(buf)
if err != nil {
log.Panicf("failed to read: %v", err)
}

fmt.Printf("read %d bytes: %s", n, string(buf[:n]))
}
14 changes: 14 additions & 0 deletions examples/pushfd/wasi-pushfd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package main

import (
"testing"

"github.com/tetratelabs/wazero/internal/testing/maintester"
"github.com/tetratelabs/wazero/internal/testing/require"
)

func Test_main(t *testing.T) {
stdout, _ := maintester.TestMain(t, main, "")
require.Equal(t, `read 12 bytes: hello world
`, stdout)
}
24 changes: 24 additions & 0 deletions experimental/wazerotest/w_wazerotest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2023 The WATER Authors. All rights reserved.
// Use of this source code is governed by Apache 2 license
// that can be found in the LICENSE file.

package wazerotest

import (
"net"
"os"
)

// TODO: implement the extended functions

func (m *Module) InsertTCPConn(*net.TCPConn) (key int32, ok bool) {
return 0, false
}

func (m *Module) InsertTCPListener(*net.TCPListener) (key int32, ok bool) {
return 0, false
}

func (m *Module) InsertOSFile(*os.File) (key int32, ok bool) {
return 0, false
}
25 changes: 0 additions & 25 deletions internal/descriptor/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package descriptor

import (
"math/bits"
"sync"
)

// Table is a data structure mapping 32 bit descriptor to items.
Expand All @@ -27,14 +26,10 @@ import (
type Table[Key ~int32, Item any] struct {
masks []uint64
items []Item
rw sync.RWMutex // protects both masks and items
}

// Len returns the number of items stored in the table.
func (t *Table[Key, Item]) Len() (n int) {
t.rw.RLock()
defer t.rw.RUnlock()

// We could make this a O(1) operation if we cached the number of items in
// the table. More state usually means more problems, so until we have a
// clear need for this, the simple implementation may be a better trade off.
Expand All @@ -46,8 +41,6 @@ func (t *Table[Key, Item]) Len() (n int) {

// grow ensures that t has enough room for n items, potentially reallocating the
// internal buffers if their capacity was too small to hold this many items.
//
// Must be called with t.rw.Lock() held.
func (t *Table[Key, Item]) grow(n int) {
// Round up to a multiple of 64 since this is the smallest increment due to
// using 64 bits masks.
Expand All @@ -71,9 +64,6 @@ func (t *Table[Key, Item]) grow(n int) {
// The method does not perform deduplication, it is possible for the same item
// to be inserted multiple times, each insertion will return a different key.
func (t *Table[Key, Item]) Insert(item Item) (key Key, ok bool) {
t.rw.Lock()
defer t.rw.Unlock()

offset := 0
insert:
// Note: this loop could be made a lot more efficient using vectorized
Expand Down Expand Up @@ -106,9 +96,6 @@ func (t *Table[Key, Item]) Lookup(key Key) (item Item, found bool) {
return
}

t.rw.RLock()
defer t.rw.RUnlock()

if i := int(key); i >= 0 && i < len(t.items) {
index := uint(key) / 64
shift := uint(key) % 64
Expand All @@ -126,9 +113,6 @@ func (t *Table[Key, Item]) InsertAt(item Item, key Key) bool {
return false
}

t.rw.Lock()
defer t.rw.Unlock()

if diff := int(key) - t.Len(); diff > 0 {
t.grow(diff)
}
Expand All @@ -145,9 +129,6 @@ func (t *Table[Key, Item]) Delete(key Key) {
return
}

t.rw.Lock()
defer t.rw.Unlock()

if index, shift := key/64, key%64; int(index) < len(t.masks) {
mask := t.masks[index]
if (mask & (1 << shift)) != 0 {
Expand All @@ -161,9 +142,6 @@ func (t *Table[Key, Item]) Delete(key Key) {
// Range calls f for each item and its associated key in the table. The function
// f might return false to interupt the iteration.
func (t *Table[Key, Item]) Range(f func(Key, Item) bool) {
t.rw.RLock()
defer t.rw.RUnlock()

for i, mask := range t.masks {
if mask == 0 {
continue
Expand All @@ -181,9 +159,6 @@ func (t *Table[Key, Item]) Range(f func(Key, Item) bool) {

// Reset clears the content of the table.
func (t *Table[Key, Item]) Reset() {
t.rw.Lock()
defer t.rw.Unlock()

for i := range t.masks {
t.masks[i] = 0
}
Expand Down
Loading