github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/sys/error.go (about) 1 // Package sys includes constants and types used by both public and internal APIs. 2 package sys 3 4 import ( 5 "context" 6 "fmt" 7 ) 8 9 // These two special exit codes are reserved by wazero for context Cancel and Timeout integrations. 10 // The assumption here is that well-behaving Wasm programs won't use these two exit codes. 11 const ( 12 // ExitCodeContextCanceled corresponds to context.Canceled and returned by ExitError.ExitCode in that case. 13 ExitCodeContextCanceled uint32 = 0xffffffff 14 // ExitCodeDeadlineExceeded corresponds to context.DeadlineExceeded and returned by ExitError.ExitCode in that case. 15 ExitCodeDeadlineExceeded uint32 = 0xefffffff 16 ) 17 18 // ExitError is returned to a caller of api.Function when api.Module CloseWithExitCode was invoked, 19 // or context.Context passed to api.Function Call was canceled or reached the Timeout. 20 // 21 // ExitCode zero value means success while any other value is an error. 22 // 23 // Here's an example of how to get the exit code: 24 // 25 // main := module.ExportedFunction("main") 26 // if err := main(ctx); err != nil { 27 // if exitErr, ok := err.(*sys.ExitError); ok { 28 // // This means your module exited with non-zero code! 29 // } 30 // --snip-- 31 // 32 // Note: While possible the reason of this was "proc_exit" from "wasi_snapshot_preview1", it could be from other host 33 // functions, for example an AssemblyScript's abort handler, or any arbitrary caller of CloseWithExitCode. 34 // 35 // See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#proc_exit and 36 // https://www.assemblyscript.org/concepts.html#special-imports 37 // 38 // Note: In the case of context cancellation or timeout, the api.Module from which the api.Function created is closed. 39 type ExitError struct { 40 // Note: this is a struct not a uint32 type as it was originally one and 41 // we don't want to break call-sites that cast into it. 42 exitCode uint32 43 } 44 45 var exitZero = &ExitError{} 46 47 func NewExitError(exitCode uint32) *ExitError { 48 if exitCode == 0 { 49 return exitZero 50 } 51 return &ExitError{exitCode: exitCode} 52 } 53 54 // ExitCode returns zero on success, and an arbitrary value otherwise. 55 func (e *ExitError) ExitCode() uint32 { 56 return e.exitCode 57 } 58 59 // Error implements the error interface. 60 func (e *ExitError) Error() string { 61 switch e.exitCode { 62 case ExitCodeContextCanceled: 63 return fmt.Sprintf("module closed with %s", context.Canceled) 64 case ExitCodeDeadlineExceeded: 65 return fmt.Sprintf("module closed with %s", context.DeadlineExceeded) 66 default: 67 return fmt.Sprintf("module closed with exit_code(%d)", e.exitCode) 68 } 69 } 70 71 // Is allows use via errors.Is 72 func (e *ExitError) Is(err error) bool { 73 if target, ok := err.(*ExitError); ok { 74 return e.exitCode == target.exitCode 75 } 76 if e.exitCode == ExitCodeContextCanceled && err == context.Canceled { 77 return true 78 } 79 if e.exitCode == ExitCodeDeadlineExceeded && err == context.DeadlineExceeded { 80 return true 81 } 82 return false 83 }