diff options
| author | nsfisis <nsfisis@gmail.com> | 2024-08-02 19:16:58 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2024-08-03 18:56:00 +0900 |
| commit | f70d6eed9f2c519aed030c9dbda99ed0435991a0 (patch) | |
| tree | 2c4e95392c88fcdbccafe62e42b651f4fbde0491 /worker/exec.go | |
| parent | db06c9332776b41b3fef537f9e6d76d38f0463b3 (diff) | |
| download | iosdc-japan-2025-albatross-f70d6eed9f2c519aed030c9dbda99ed0435991a0.tar.gz iosdc-japan-2025-albatross-f70d6eed9f2c519aed030c9dbda99ed0435991a0.tar.zst iosdc-japan-2025-albatross-f70d6eed9f2c519aed030c9dbda99ed0435991a0.zip | |
feat: implement worker
Diffstat (limited to 'worker/exec.go')
| -rw-r--r-- | worker/exec.go | 186 |
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, + } +} |
