diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-09-05 20:34:02 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-09-05 20:52:59 +0900 |
| commit | dd1c68425120fca008a3b10991c865ea586c7002 (patch) | |
| tree | 13784e4e2923d7a51a63ba148c89907ef73cce6f /worker/swift/exec.go | |
| parent | c7941d027be068f6e563a17e882232580fe15334 (diff) | |
| download | iosdc-japan-2025-albatross-dd1c68425120fca008a3b10991c865ea586c7002.tar.gz iosdc-japan-2025-albatross-dd1c68425120fca008a3b10991c865ea586c7002.tar.zst iosdc-japan-2025-albatross-dd1c68425120fca008a3b10991c865ea586c7002.zip | |
feat(worker): add swift worker
Diffstat (limited to 'worker/swift/exec.go')
| -rw-r--r-- | worker/swift/exec.go | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/worker/swift/exec.go b/worker/swift/exec.go new file mode 100644 index 0000000..37f542b --- /dev/null +++ b/worker/swift/exec.go @@ -0,0 +1,180 @@ +package main + +import ( + "bytes" + "context" + "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 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, isCompile bool) string { + if err != nil { + if err == context.DeadlineExceeded { + return resultTimeout + } + if isCompile { + return resultCompileError + } + return resultRuntimeError + } + return resultSuccess +} + +func execSwiftCompile( + ctx context.Context, + code string, + codeHash string, + maxDuration time.Duration, +) swiftCompileResponseData { + inPath := calcFilePath(codeHash, "swift") + outPath := calcFilePath(codeHash, "wasm") + + if err := os.WriteFile(inPath, []byte(code), 0644); err != nil { + return swiftCompileResponseData{ + Status: 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{ + Status: convertCommandErrorToResultType(err, true), + Stdout: stdout, + Stderr: stderr, + } +} + +func execWasmCompile( + ctx context.Context, + codeHash string, + maxDuration time.Duration, +) wasmCompileResponseData { + inPath := calcFilePath(codeHash, "wasm") + outPath := calcFilePath(codeHash, "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{ + Status: convertCommandErrorToResultType(err, true), + Stdout: stdout, + Stderr: stderr, + } +} + +func execTestRun( + ctx context.Context, + codeHash string, + stdin string, + maxDuration time.Duration, +) testRunResponseData { + inPath := calcFilePath(codeHash, "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{ + Status: convertCommandErrorToResultType(err, false), + Stdout: stdout, + Stderr: stderr, + } +} |
