Skip to content

Adds once mechanism for Go 1.21 GOOS=js #79

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

Closed
wants to merge 4 commits into from

Conversation

codefromthecrypt
Copy link
Contributor

@codefromthecrypt codefromthecrypt commented Aug 8, 2023

This adds an alternative to pooling called once. Specifically, this uses a module once per request, and in doing so supports guests who cannot export functions. The implementation adds await_response with adapter glue which the host side implements with Go channels.

As you can see below, this is far less performant than using the pool model. However, this allows use of Go 1.21 GOOS=wasip1, which does not support pooling because it cannot export host functions.

Benchmark/example_log_once
Benchmark/example_log_once/example_log_once
Benchmark/example_log_once/example_log_once-12                                 	   52648	     23117 ns/op
Benchmark/example_log
Benchmark/example_log/example_log
Benchmark/example_log/example_log-12                                           	 2100068	       550.4 ns/op

Below is generic and can convert any normal handler to a single synchronous call, provided $handle_request and $handle_response are not exported. ;)

  ;; import $await_response, which blocks until the response is ready.
  (import "http_handler" "await_response" (func $await_response
    (param $ctx_next i64)
    (result (; is_error ;) i32)))

  ;; define a start function that performs a request-response without exports.
  ;; note: this logic is generic and can convert any exported $handle_request/
  ;; $handle_response pair to a synchronous call without exports.
  (func $start
    (local $ctx_next i64)
    (local $is_error i32)
    (local $ctx i32)

    ;; ctxNext := handleRequest()
    (local.set $ctx_next (call $handle_request))

    ;; isError := awaitResponse(ctxNext)
    (local.set $is_error (call $await_response (local.get $ctx_next)))

    ;; ctx := uint32(ctxNext >> 32)
    (local.set $ctx
      (i32.wrap_i64 (i64.shr_u (local.get $ctx_next) (i64.const 32))))

    ;; handleResponse(ctx, isError)
    (call $handle_response (local.get $ctx) (local.get $is_error))
  )

See golang/go#42372

Adrian Cole added 4 commits August 8, 2023 12:08
This adds an alternative to pooling called once. Specifically, this uses
a module once per request, and in doing so supports guests who cannot
export functions. The implementation adds `await_response` with adapter
glue which the host side implements with Go channels.

As you can see below, this is far less performant than using the pool
model. However, this allows use of Go 1.21 GOOS=wasip1, which does not
support pooling because it cannot export host functions.
```
Benchmark/example_log_once
Benchmark/example_log_once/example_log_once
Benchmark/example_log_once/example_log_once-12                                 	   52648	     23117 ns/op
Benchmark/example_log
Benchmark/example_log/example_log
Benchmark/example_log/example_log-12                                           	 2100068	       550.4 ns/op
```

See golang/go#42372

Signed-off-by: Adrian Cole <[email protected]>
Signed-off-by: Adrian Cole <[email protected]>
Signed-off-by: Adrian Cole <[email protected]>
Signed-off-by: Adrian Cole <[email protected]>
@codefromthecrypt
Copy link
Contributor Author

closing this. If someone wants to follow-up, basically, the host would need to enter an event loop and use two functions to decide which state the VM is in. It would change "once mode" to a different style of "pool mode"

(; begin adapter logic

   Below is generic and can convert any normal handler to a single synchronous
   call, provided $handle_request and $handle_response are not exported. ;)

  ;; import $await_request, which blocks until a request is ready.
  (import "http_handler" "await_request" (func $await_request
    (result (; stop ;) i32)))

  ;; import $await_response, which blocks until a response is ready.
  (import "http_handler" "await_response" (func $await_response
    (param $ctx_next i64)
    (result (; is_error ;) i32)))

  ;; define a start function that performs a request-response without exports.
  ;; note: this logic is generic and can convert any exported $handle_request/
  ;; $handle_response pair to a synchronous call without exports.
  (func $start
    (local $stop i32)
    (local $ctx_next i64)
    (local $is_error i32)
    (local $ctx i32)

    (loop $round_trip
      ;; for awaitRequest != 0 {
      (if (i32.eqz (call $await_request))
        (then
          ;; ctxNext := handleRequest()
          (local.set $ctx_next (call $handle_request))

          ;; isError := awaitResponse(ctxNext)
          (local.set $is_error (call $await_response (local.get $ctx_next)))

          ;; ctx := uint32(ctxNext >> 32)
          (local.set $ctx
            (i32.wrap_i64 (i64.shr_u (local.get $ctx_next) (i64.const 32))))

          ;; handleResponse(ctx, isError)
          (call $handle_response (local.get $ctx) (local.get $is_error))
          (br $round_trip)))))

(; end adapter logic ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant