aboutsummaryrefslogtreecommitdiffhomepage
path: root/worker/swift/exec.go
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-09-05 20:34:02 +0900
committernsfisis <nsfisis@gmail.com>2025-09-05 20:52:59 +0900
commitdd1c68425120fca008a3b10991c865ea586c7002 (patch)
tree13784e4e2923d7a51a63ba148c89907ef73cce6f /worker/swift/exec.go
parentc7941d027be068f6e563a17e882232580fe15334 (diff)
downloadiosdc-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.go180
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,
+ }
+}