aboutsummaryrefslogtreecommitdiffhomepage
path: root/worker/exec.go
diff options
context:
space:
mode:
Diffstat (limited to 'worker/exec.go')
-rw-r--r--worker/exec.go186
1 files changed, 186 insertions, 0 deletions
diff --git a/worker/exec.go b/worker/exec.go
new file mode 100644
index 0000000..2ef16fa
--- /dev/null
+++ b/worker/exec.go
@@ -0,0 +1,186 @@
+package main
+
+import (
+ "bytes"
+ "context"
+ "crypto/md5"
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+ "time"
+)
+
+const (
+ dataRootDir = "/app/data"
+ // Stores *.swift files.
+ dataSwiftRootDir = dataRootDir + "/swift"
+ // Stores *.wasm files.
+ dataWasmRootDir = dataRootDir + "/wasm"
+ // Stores *.cwasm files (compiled wasm generated by "wasmtime compile").
+ dataCwasmRootDir = dataRootDir + "/cwasm"
+
+ wasmMaxMemorySize = 10 * 1024 * 1024 // 10 MiB
+)
+
+func prepareDirectories() error {
+ if err := os.MkdirAll(dataSwiftRootDir, 0755); err != nil {
+ return err
+ }
+ if err := os.MkdirAll(dataWasmRootDir, 0755); err != nil {
+ return err
+ }
+ if err := os.MkdirAll(dataCwasmRootDir, 0755); err != nil {
+ return err
+ }
+ return nil
+}
+
+func calcHash(code string) string {
+ return fmt.Sprintf("%x", md5.Sum([]byte(code)))
+}
+
+func calcFilePath(hash, ext string) string {
+ return fmt.Sprintf("%s/%s/%s.%s", dataRootDir, ext, hash, ext)
+}
+
+func execCommandWithTimeout(
+ ctx context.Context,
+ maxDuration time.Duration,
+ makeCmd func(context.Context) *exec.Cmd,
+) (string, string, error) {
+ ctx, cancel := context.WithTimeout(ctx, maxDuration)
+ defer cancel()
+
+ cmd := makeCmd(ctx)
+
+ var stdout bytes.Buffer
+ var stderr bytes.Buffer
+ cmd.Stdout = &stdout
+ cmd.Stderr = &stderr
+
+ exitCh := make(chan error)
+ go func() {
+ exitCh <- cmd.Run()
+ }()
+
+ select {
+ case <-ctx.Done():
+ return stdout.String(), stderr.String(), ctx.Err()
+ case err := <-exitCh:
+ return stdout.String(), stderr.String(), err
+ }
+}
+
+func convertCommandErrorToResultType(err error) string {
+ if err != nil {
+ if err == context.DeadlineExceeded {
+ return resultTimeout
+ } else {
+ return resultFailure
+ }
+ } else {
+ return resultSuccess
+ }
+}
+
+func execSwiftCompile(
+ ctx context.Context,
+ code string,
+ maxDuration time.Duration,
+) swiftCompileResponseData {
+ hash := calcHash(code)
+ inPath := calcFilePath(hash, "swift")
+ outPath := calcFilePath(hash, "wasm")
+
+ if err := os.WriteFile(inPath, []byte(code), 0644); err != nil {
+ return swiftCompileResponseData{
+ Result: resultInternalError,
+ Stdout: "",
+ Stderr: err.Error(),
+ }
+ }
+
+ stdout, stderr, err := execCommandWithTimeout(
+ ctx,
+ maxDuration,
+ func(ctx context.Context) *exec.Cmd {
+ return exec.CommandContext(
+ ctx,
+ "swiftc",
+ "-target", "wasm32-unknown-wasi",
+ "-o", outPath,
+ inPath,
+ )
+ },
+ )
+
+ return swiftCompileResponseData{
+ Result: convertCommandErrorToResultType(err),
+ Stdout: stdout,
+ Stderr: stderr,
+ }
+}
+
+func execWasmCompile(
+ ctx context.Context,
+ code string,
+ maxDuration time.Duration,
+) wasmCompileResponseData {
+ hash := calcHash(code)
+ inPath := calcFilePath(hash, "wasm")
+ outPath := calcFilePath(hash, "cwasm")
+
+ stdout, stderr, err := execCommandWithTimeout(
+ ctx,
+ maxDuration,
+ func(ctx context.Context) *exec.Cmd {
+ return exec.CommandContext(
+ ctx,
+ "wasmtime", "compile",
+ "-O", "opt-level=0",
+ "-C", "cache=n",
+ "-W", fmt.Sprintf("max-memory-size=%d", wasmMaxMemorySize),
+ "-o", outPath,
+ inPath,
+ )
+ },
+ )
+
+ return wasmCompileResponseData{
+ Result: convertCommandErrorToResultType(err),
+ Stdout: stdout,
+ Stderr: stderr,
+ }
+}
+
+func execTestRun(
+ ctx context.Context,
+ code string,
+ stdin string,
+ maxDuration time.Duration,
+) testRunResponseData {
+ hash := calcHash(code)
+ inPath := calcFilePath(hash, "cwasm")
+
+ stdout, stderr, err := execCommandWithTimeout(
+ ctx,
+ maxDuration,
+ func(ctx context.Context) *exec.Cmd {
+ cmd := exec.CommandContext(
+ ctx,
+ "wasmtime", "run",
+ "--allow-precompiled",
+ inPath,
+ )
+ cmd.Stdin = strings.NewReader(stdin)
+ return cmd
+ },
+ )
+
+ return testRunResponseData{
+ Result: convertCommandErrorToResultType(err),
+ Stdout: stdout,
+ Stderr: stderr,
+ }
+}