improved faster backend with input output
This commit is contained in:
@@ -1 +1 @@
|
|||||||
SERVER_URL="http://localhost:8000"
|
VITE_API_URL="https://monacoapi.thearnab.tech"
|
||||||
@@ -714,7 +714,7 @@ Happy coding!`;
|
|||||||
title="Run code"
|
title="Run code"
|
||||||
>
|
>
|
||||||
{isRunning ? <Loader size={16} className="animate-spin" /> : <Play size={16} />}
|
{isRunning ? <Loader size={16} className="animate-spin" /> : <Play size={16} />}
|
||||||
<span>Run</span>
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="terminal-toggle-button"
|
className="terminal-toggle-button"
|
||||||
|
|||||||
@@ -836,10 +836,11 @@ body {
|
|||||||
.run-button {
|
.run-button {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 4px 8px;
|
padding: 4px 4px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
background-color: #3c3c3c;
|
background-color: #1e1e1e;
|
||||||
color: #cccccc;
|
color: #cccccc;
|
||||||
|
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/arnab-afk/monaco/model"
|
"github.com/arnab-afk/monaco/model"
|
||||||
"github.com/arnab-afk/monaco/service"
|
"github.com/arnab-afk/monaco/service"
|
||||||
@@ -79,7 +80,34 @@ func (h *Handler) StatusHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
json.NewEncoder(w).Encode(map[string]string{"status": submission.Status})
|
// Return status with time information
|
||||||
|
response := map[string]interface{}{
|
||||||
|
"id": submission.ID,
|
||||||
|
"status": submission.Status,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add time information based on status
|
||||||
|
if !submission.QueuedAt.IsZero() {
|
||||||
|
response["queuedAt"] = submission.QueuedAt.Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
|
||||||
|
if submission.Status == "running" && !submission.StartedAt.IsZero() {
|
||||||
|
response["startedAt"] = submission.StartedAt.Format(time.RFC3339)
|
||||||
|
response["runningFor"] = time.Since(submission.StartedAt).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if submission.Status == "completed" || submission.Status == "failed" {
|
||||||
|
if !submission.CompletedAt.IsZero() && !submission.StartedAt.IsZero() {
|
||||||
|
response["executionTime"] = submission.CompletedAt.Sub(submission.StartedAt).Milliseconds()
|
||||||
|
response["completedAt"] = submission.CompletedAt.Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
|
http.Error(w, "Failed to serialize response: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResultHandler handles result requests
|
// ResultHandler handles result requests
|
||||||
@@ -99,7 +127,56 @@ func (h *Handler) ResultHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
json.NewEncoder(w).Encode(map[string]string{"output": submission.Output})
|
// Prepare response with safe time handling
|
||||||
|
response := map[string]interface{}{
|
||||||
|
"id": submission.ID,
|
||||||
|
"status": submission.Status,
|
||||||
|
"output": submission.Output,
|
||||||
|
"language": submission.Language,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only include time fields if they're set
|
||||||
|
if !submission.QueuedAt.IsZero() {
|
||||||
|
response["queuedAt"] = submission.QueuedAt.Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !submission.StartedAt.IsZero() {
|
||||||
|
response["startedAt"] = submission.StartedAt.Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !submission.CompletedAt.IsZero() {
|
||||||
|
response["completedAt"] = submission.CompletedAt.Format(time.RFC3339)
|
||||||
|
|
||||||
|
// Calculate times only if we have valid timestamps
|
||||||
|
if !submission.StartedAt.IsZero() {
|
||||||
|
executionTime := submission.CompletedAt.Sub(submission.StartedAt)
|
||||||
|
response["executionTime"] = executionTime.Milliseconds() // Use milliseconds for frontend
|
||||||
|
response["executionTimeFormatted"] = executionTime.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !submission.QueuedAt.IsZero() {
|
||||||
|
totalTime := submission.CompletedAt.Sub(submission.QueuedAt)
|
||||||
|
response["totalTime"] = totalTime.Milliseconds() // Use milliseconds for frontend
|
||||||
|
response["totalTimeFormatted"] = totalTime.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return full submission details
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
|
http.Error(w, "Failed to serialize response: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueueStatsHandler provides information about the job queue
|
||||||
|
func (h *Handler) QueueStatsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
stats := h.executionService.GetQueueStats()
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
"queue_stats": stats,
|
||||||
|
"submissions": len(h.submissions),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateID creates a unique ID for submissions
|
// generateID creates a unique ID for submissions
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ func main() {
|
|||||||
http.HandleFunc("/submit", corsMiddleware(loggingMiddleware(h.SubmitHandler)))
|
http.HandleFunc("/submit", corsMiddleware(loggingMiddleware(h.SubmitHandler)))
|
||||||
http.HandleFunc("/status", corsMiddleware(loggingMiddleware(h.StatusHandler)))
|
http.HandleFunc("/status", corsMiddleware(loggingMiddleware(h.StatusHandler)))
|
||||||
http.HandleFunc("/result", corsMiddleware(loggingMiddleware(h.ResultHandler)))
|
http.HandleFunc("/result", corsMiddleware(loggingMiddleware(h.ResultHandler)))
|
||||||
|
http.HandleFunc("/queue-stats", corsMiddleware(loggingMiddleware(h.QueueStatsHandler)))
|
||||||
|
|
||||||
port := ":8080"
|
port := ":8080"
|
||||||
log.Printf("Server started at %s", port)
|
log.Printf("Server started at %s", port)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ type CodeSubmission struct {
|
|||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
|
Input string `json:"input"` // Added input field for stdin
|
||||||
Status string `json:"status"` // "queued", "running", "completed", "failed"
|
Status string `json:"status"` // "queued", "running", "completed", "failed"
|
||||||
QueuedAt time.Time `json:"queuedAt"`
|
QueuedAt time.Time `json:"queuedAt"`
|
||||||
StartedAt time.Time `json:"startedAt,omitempty"`
|
StartedAt time.Time `json:"startedAt,omitempty"`
|
||||||
|
|||||||
BIN
backend/monaco
Normal file
BIN
backend/monaco
Normal file
Binary file not shown.
@@ -2,10 +2,13 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -61,6 +64,19 @@ func (s *ExecutionService) ExecuteCode(submission *model.CodeSubmission) {
|
|||||||
log.Printf("[SUBMISSION-%s] Code submission queued for language: %s (Queue length: %d)",
|
log.Printf("[SUBMISSION-%s] Code submission queued for language: %s (Queue length: %d)",
|
||||||
submission.ID, submission.Language, s.queue.QueueStats()["queue_length"])
|
submission.ID, submission.Language, s.queue.QueueStats()["queue_length"])
|
||||||
|
|
||||||
|
// Log if input is provided
|
||||||
|
if len(submission.Input) > 0 {
|
||||||
|
inputLen := len(submission.Input)
|
||||||
|
previewLen := 30
|
||||||
|
if inputLen > previewLen {
|
||||||
|
log.Printf("[INPUT-%s] Input provided (%d bytes): %s...",
|
||||||
|
submission.ID, inputLen, submission.Input[:previewLen])
|
||||||
|
} else {
|
||||||
|
log.Printf("[INPUT-%s] Input provided (%d bytes): %s",
|
||||||
|
submission.ID, inputLen, submission.Input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
job := NewCodeExecutionJob(s, submission)
|
job := NewCodeExecutionJob(s, submission)
|
||||||
s.queue.Enqueue(job)
|
s.queue.Enqueue(job)
|
||||||
}
|
}
|
||||||
@@ -90,15 +106,323 @@ func (s *ExecutionService) executeLanguageSpecific(submission *model.CodeSubmiss
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetQueueStats returns the current queue statistics
|
// executeWithInput runs a command with a timeout and provides input
|
||||||
func (s *ExecutionService) GetQueueStats() map[string]int {
|
func (s *ExecutionService) executeWithInput(cmd *exec.Cmd, input string, timeout time.Duration, submissionID string) ([]byte, error) {
|
||||||
stats := s.queue.QueueStats()
|
log.Printf("[TIMEOUT-%s] Setting execution timeout: %v", submissionID, timeout)
|
||||||
log.Printf("[QUEUE] Stats - Jobs in queue: %d, Running jobs: %d, Max workers: %d",
|
|
||||||
stats["queue_length"], stats["running_jobs"], stats["max_workers"])
|
// Set up input pipe if input is provided
|
||||||
return stats
|
if input != "" {
|
||||||
|
stdin, err := cmd.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR-%s] Failed to create stdin pipe: %v", submissionID, err)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a timeout function
|
// Write input in a goroutine to avoid blocking
|
||||||
|
go func() {
|
||||||
|
defer stdin.Close()
|
||||||
|
io.WriteString(stdin, input)
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Printf("[INPUT-%s] Providing input to process", submissionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
var output []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Printf("[EXEC-%s] Starting command execution: %v", submissionID, cmd.Args)
|
||||||
|
output, err = cmd.CombinedOutput()
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-time.After(timeout):
|
||||||
|
log.Printf("[TIMEOUT-%s] Execution timed out after %v seconds", submissionID, timeout.Seconds())
|
||||||
|
if err := cmd.Process.Kill(); err != nil {
|
||||||
|
log.Printf("[TIMEOUT-%s] Failed to kill process: %v", submissionID, err)
|
||||||
|
return nil, fmt.Errorf("timeout reached but failed to kill process: %v", err)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("execution timed out after %v seconds", timeout.Seconds())
|
||||||
|
case <-done:
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[EXEC-%s] Command execution failed: %v", submissionID, err)
|
||||||
|
} else {
|
||||||
|
log.Printf("[EXEC-%s] Command execution completed successfully", submissionID)
|
||||||
|
}
|
||||||
|
return output, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// executePython runs Python code in a container
|
||||||
|
func (s *ExecutionService) executePython(submission *model.CodeSubmission) {
|
||||||
|
log.Printf("[PYTHON-%s] Preparing Python execution environment", submission.ID)
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
cmd := exec.Command("docker", "run", "--rm", "-i",
|
||||||
|
"--network=none", // No network access
|
||||||
|
"--memory=100m", // Memory limit
|
||||||
|
"--cpu-period=100000", // CPU quota period
|
||||||
|
"--cpu-quota=10000", // 10% CPU
|
||||||
|
"--ulimit", "nofile=64:64", // File descriptor limits
|
||||||
|
"python:3.9", "python", "-c", submission.Code)
|
||||||
|
|
||||||
|
log.Printf("[PYTHON-%s] Executing Python code with timeout: 10s", submission.ID)
|
||||||
|
var output []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if submission.Input != "" {
|
||||||
|
cmd.Stdin = strings.NewReader(submission.Input)
|
||||||
|
output, err = cmd.CombinedOutput()
|
||||||
|
} else {
|
||||||
|
output, err = s.executeWithTimeout(cmd, 10*time.Second, submission.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsed := time.Since(startTime)
|
||||||
|
log.Printf("[PYTHON-%s] Python execution completed in %v", submission.ID, elapsed)
|
||||||
|
|
||||||
|
s.updateSubmissionResult(submission, output, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractClassName extracts the Java class name from code
|
||||||
|
func extractClassName(code string) string {
|
||||||
|
// Default class name as fallback
|
||||||
|
defaultClass := "Solution"
|
||||||
|
|
||||||
|
// Look for public class
|
||||||
|
re := regexp.MustCompile(`public\s+class\s+(\w+)`)
|
||||||
|
matches := re.FindStringSubmatch(code)
|
||||||
|
if len(matches) > 1 {
|
||||||
|
return matches[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for any class if no public class
|
||||||
|
re = regexp.MustCompile(`class\s+(\w+)`)
|
||||||
|
matches = re.FindStringSubmatch(code)
|
||||||
|
if len(matches) > 1 {
|
||||||
|
return matches[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultClass
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeJava runs Java code in a container
|
||||||
|
func (s *ExecutionService) executeJava(submission *model.CodeSubmission) {
|
||||||
|
log.Printf("[JAVA-%s] Preparing Java execution environment", submission.ID)
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
// Extract class name from code
|
||||||
|
className := extractClassName(submission.Code)
|
||||||
|
log.Printf("[JAVA-%s] Detected class name: %s", submission.ID, className)
|
||||||
|
|
||||||
|
// Create temp directory for Java files
|
||||||
|
tempDir, err := os.MkdirTemp("", "java-execution-"+submission.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[JAVA-%s] Failed to create temp directory: %v", submission.ID, err)
|
||||||
|
submission.Status = "failed"
|
||||||
|
submission.Output = "Failed to create temp directory: " + err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tempDir)
|
||||||
|
log.Printf("[JAVA-%s] Created temp directory: %s", submission.ID, tempDir)
|
||||||
|
|
||||||
|
// Write Java code to file with detected class name
|
||||||
|
javaFilePath := filepath.Join(tempDir, className+".java")
|
||||||
|
if err := os.WriteFile(javaFilePath, []byte(submission.Code), 0644); err != nil {
|
||||||
|
log.Printf("[JAVA-%s] Failed to write Java file: %v", submission.ID, err)
|
||||||
|
submission.Status = "failed"
|
||||||
|
submission.Output = "Failed to write Java file: " + err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("[JAVA-%s] Wrote code to file: %s", submission.ID, javaFilePath)
|
||||||
|
|
||||||
|
// First compile without running
|
||||||
|
compileCmd := exec.Command("docker", "run", "--rm",
|
||||||
|
"-v", tempDir+":/code", // Mount code directory
|
||||||
|
"eclipse-temurin:11-jdk-alpine",
|
||||||
|
"javac", "/code/"+className+".java")
|
||||||
|
|
||||||
|
log.Printf("[JAVA-%s] Compiling Java code", submission.ID)
|
||||||
|
compileOutput, compileErr := compileCmd.CombinedOutput()
|
||||||
|
|
||||||
|
if compileErr != nil {
|
||||||
|
log.Printf("[JAVA-%s] Compilation failed: %v", submission.ID, compileErr)
|
||||||
|
submission.Status = "failed"
|
||||||
|
submission.Output = "Compilation error:\n" + string(compileOutput)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[JAVA-%s] Compilation successful", submission.ID)
|
||||||
|
|
||||||
|
// Now run the compiled class
|
||||||
|
runCmd := exec.Command("docker", "run", "--rm", "-i",
|
||||||
|
"--network=none", // No network access
|
||||||
|
"--memory=400m", // Memory limit
|
||||||
|
"--cpu-period=100000", // CPU quota period
|
||||||
|
"--cpu-quota=50000", // 50% CPU
|
||||||
|
"-v", tempDir+":/code", // Mount code directory
|
||||||
|
"eclipse-temurin:11-jdk-alpine",
|
||||||
|
"java", "-XX:+TieredCompilation", "-XX:TieredStopAtLevel=1",
|
||||||
|
"-Xverify:none", "-Xms64m", "-Xmx256m",
|
||||||
|
"-cp", "/code", className)
|
||||||
|
|
||||||
|
// Add input if provided
|
||||||
|
var output []byte
|
||||||
|
|
||||||
|
if submission.Input != "" {
|
||||||
|
log.Printf("[JAVA-%s] Executing Java code with input", submission.ID)
|
||||||
|
runCmd.Stdin = strings.NewReader(submission.Input)
|
||||||
|
output, err = runCmd.CombinedOutput()
|
||||||
|
} else {
|
||||||
|
log.Printf("[JAVA-%s] Executing Java code without input", submission.ID)
|
||||||
|
output, err = s.executeWithTimeout(runCmd, 15*time.Second, submission.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsed := time.Since(startTime)
|
||||||
|
log.Printf("[JAVA-%s] Java execution completed in %v", submission.ID, elapsed)
|
||||||
|
|
||||||
|
s.updateSubmissionResult(submission, output, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeC runs C code in a container with improved file handling
|
||||||
|
func (s *ExecutionService) executeC(submission *model.CodeSubmission) {
|
||||||
|
log.Printf("[C-%s] Preparing C execution environment", submission.ID)
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
// Create unique temp directory for C files
|
||||||
|
tempDir, err := os.MkdirTemp("", "c-execution-"+submission.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[C-%s] Failed to create temp directory: %v", submission.ID, err)
|
||||||
|
submission.Status = "failed"
|
||||||
|
submission.Output = "Failed to create temp directory: " + err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tempDir)
|
||||||
|
log.Printf("[C-%s] Created temp directory: %s", submission.ID, tempDir)
|
||||||
|
|
||||||
|
// Write C code to file
|
||||||
|
cFilePath := filepath.Join(tempDir, "solution.c")
|
||||||
|
if err := os.WriteFile(cFilePath, []byte(submission.Code), 0644); err != nil {
|
||||||
|
log.Printf("[C-%s] Failed to write C file: %v", submission.ID, err)
|
||||||
|
submission.Status = "failed"
|
||||||
|
submission.Output = "Failed to write C file: " + err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("[C-%s] Wrote code to file: %s", submission.ID, cFilePath)
|
||||||
|
|
||||||
|
// Compile C code first
|
||||||
|
compileCmd := exec.Command("docker", "run", "--rm",
|
||||||
|
"-v", tempDir+":/code", // Mount code directory
|
||||||
|
"gcc:latest", "gcc", "-o", "/code/solution", "/code/solution.c")
|
||||||
|
|
||||||
|
compileOutput, compileErr := compileCmd.CombinedOutput()
|
||||||
|
|
||||||
|
if compileErr != nil {
|
||||||
|
log.Printf("[C-%s] Compilation failed: %v", submission.ID, compileErr)
|
||||||
|
submission.Status = "failed"
|
||||||
|
submission.Output = "Compilation error:\n" + string(compileOutput)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[C-%s] Compilation successful", submission.ID)
|
||||||
|
|
||||||
|
// Run C executable
|
||||||
|
runCmd := exec.Command("docker", "run", "--rm", "-i",
|
||||||
|
"--network=none", // No network access
|
||||||
|
"--memory=100m", // Memory limit
|
||||||
|
"--cpu-period=100000", // CPU quota period
|
||||||
|
"--cpu-quota=10000", // 10% CPU
|
||||||
|
"-v", tempDir+":/code", // Mount code directory
|
||||||
|
"gcc:latest", "/code/solution")
|
||||||
|
|
||||||
|
// Add input if provided
|
||||||
|
var output []byte
|
||||||
|
// Don't redeclare err here - use the existing variable
|
||||||
|
if submission.Input != "" {
|
||||||
|
log.Printf("[C-%s] Executing C code with input", submission.ID)
|
||||||
|
runCmd.Stdin = strings.NewReader(submission.Input)
|
||||||
|
output, err = runCmd.CombinedOutput() // Use the existing err variable
|
||||||
|
} else {
|
||||||
|
log.Printf("[C-%s] Executing C code without input", submission.ID)
|
||||||
|
output, err = s.executeWithTimeout(runCmd, 10*time.Second, submission.ID) // Use the existing err variable
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsed := time.Since(startTime)
|
||||||
|
log.Printf("[C-%s] C execution completed in %v", submission.ID, elapsed)
|
||||||
|
|
||||||
|
s.updateSubmissionResult(submission, output, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeCpp runs C++ code in a container with improved file handling
|
||||||
|
func (s *ExecutionService) executeCpp(submission *model.CodeSubmission) {
|
||||||
|
log.Printf("[CPP-%s] Preparing C++ execution environment", submission.ID)
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
// Create unique temp directory for C++ files
|
||||||
|
tempDir, err := os.MkdirTemp("", "cpp-execution-"+submission.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[CPP-%s] Failed to create temp directory: %v", submission.ID, err)
|
||||||
|
submission.Status = "failed"
|
||||||
|
submission.Output = "Failed to create temp directory: " + err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tempDir)
|
||||||
|
log.Printf("[CPP-%s] Created temp directory: %s", submission.ID, tempDir)
|
||||||
|
|
||||||
|
// Write C++ code to file
|
||||||
|
cppFilePath := filepath.Join(tempDir, "solution.cpp")
|
||||||
|
if err := os.WriteFile(cppFilePath, []byte(submission.Code), 0644); err != nil {
|
||||||
|
log.Printf("[CPP-%s] Failed to write C++ file: %v", submission.ID, err)
|
||||||
|
submission.Status = "failed"
|
||||||
|
submission.Output = "Failed to write C++ file: " + err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("[CPP-%s] Wrote code to file: %s", submission.ID, cppFilePath)
|
||||||
|
|
||||||
|
// Compile C++ code first
|
||||||
|
compileCmd := exec.Command("docker", "run", "--rm",
|
||||||
|
"-v", tempDir+":/code", // Mount code directory
|
||||||
|
"gcc:latest", "g++", "-o", "/code/solution", "/code/solution.cpp")
|
||||||
|
|
||||||
|
compileOutput, compileErr := compileCmd.CombinedOutput()
|
||||||
|
|
||||||
|
if compileErr != nil {
|
||||||
|
log.Printf("[CPP-%s] Compilation failed: %v", submission.ID, compileErr)
|
||||||
|
submission.Status = "failed"
|
||||||
|
submission.Output = "Compilation error:\n" + string(compileOutput)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[CPP-%s] Compilation successful", submission.ID)
|
||||||
|
|
||||||
|
// Run C++ executable
|
||||||
|
runCmd := exec.Command("docker", "run", "--rm", "-i",
|
||||||
|
"--network=none", // No network access
|
||||||
|
"--memory=100m", // Memory limit
|
||||||
|
"--cpu-period=100000", // CPU quota period
|
||||||
|
"--cpu-quota=10000", // 10% CPU
|
||||||
|
"-v", tempDir+":/code", // Mount code directory
|
||||||
|
"gcc:latest", "/code/solution")
|
||||||
|
|
||||||
|
// Add input if provided
|
||||||
|
var output []byte
|
||||||
|
if submission.Input != "" {
|
||||||
|
log.Printf("[CPP-%s] Executing C++ code with input", submission.ID)
|
||||||
|
runCmd.Stdin = strings.NewReader(submission.Input)
|
||||||
|
output, err = runCmd.CombinedOutput()
|
||||||
|
} else {
|
||||||
|
log.Printf("[CPP-%s] Executing C++ code without input", submission.ID)
|
||||||
|
output, err = s.executeWithTimeout(runCmd, 10*time.Second, submission.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsed := time.Since(startTime)
|
||||||
|
log.Printf("[CPP-%s] C++ execution completed in %v", submission.ID, elapsed)
|
||||||
|
|
||||||
|
s.updateSubmissionResult(submission, output, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeWithTimeout runs a command with a timeout
|
||||||
func (s *ExecutionService) executeWithTimeout(cmd *exec.Cmd, timeout time.Duration, submissionID string) ([]byte, error) {
|
func (s *ExecutionService) executeWithTimeout(cmd *exec.Cmd, timeout time.Duration, submissionID string) ([]byte, error) {
|
||||||
log.Printf("[TIMEOUT-%s] Setting execution timeout: %v", submissionID, timeout)
|
log.Printf("[TIMEOUT-%s] Setting execution timeout: %v", submissionID, timeout)
|
||||||
|
|
||||||
@@ -130,161 +454,6 @@ func (s *ExecutionService) executeWithTimeout(cmd *exec.Cmd, timeout time.Durati
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// executePython runs Python code in a container
|
|
||||||
func (s *ExecutionService) executePython(submission *model.CodeSubmission) {
|
|
||||||
log.Printf("[PYTHON-%s] Preparing Python execution environment", submission.ID)
|
|
||||||
startTime := time.Now()
|
|
||||||
|
|
||||||
cmd := exec.Command("docker", "run", "--rm", "-i",
|
|
||||||
"--network=none", // No network access
|
|
||||||
"--memory=100m", // Memory limit
|
|
||||||
"--cpu-period=100000", // CPU quota period
|
|
||||||
"--cpu-quota=10000", // 10% CPU
|
|
||||||
"--ulimit", "nofile=64:64", // File descriptor limits
|
|
||||||
"python:3.9", "python", "-c", submission.Code)
|
|
||||||
|
|
||||||
log.Printf("[PYTHON-%s] Executing Python code with timeout: 10s", submission.ID)
|
|
||||||
output, err := s.executeWithTimeout(cmd, 10*time.Second, submission.ID)
|
|
||||||
|
|
||||||
elapsed := time.Since(startTime)
|
|
||||||
log.Printf("[PYTHON-%s] Python execution completed in %v", submission.ID, elapsed)
|
|
||||||
|
|
||||||
s.updateSubmissionResult(submission, output, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// executeJava runs Java code in a container
|
|
||||||
func (s *ExecutionService) executeJava(submission *model.CodeSubmission) {
|
|
||||||
log.Printf("[JAVA-%s] Preparing Java execution environment", submission.ID)
|
|
||||||
startTime := time.Now()
|
|
||||||
|
|
||||||
// Create temp directory for Java files
|
|
||||||
tempDir, err := os.MkdirTemp("", "java-execution")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[JAVA-%s] Failed to create temp directory: %v", submission.ID, err)
|
|
||||||
submission.Status = "failed"
|
|
||||||
submission.Output = "Failed to create temp directory: " + err.Error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tempDir)
|
|
||||||
log.Printf("[JAVA-%s] Created temp directory: %s", submission.ID, tempDir)
|
|
||||||
|
|
||||||
// Write Java code to file
|
|
||||||
javaFilePath := filepath.Join(tempDir, "Main.java")
|
|
||||||
if err := os.WriteFile(javaFilePath, []byte(submission.Code), 0644); err != nil {
|
|
||||||
log.Printf("[JAVA-%s] Failed to write Java file: %v", submission.ID, err)
|
|
||||||
submission.Status = "failed"
|
|
||||||
submission.Output = "Failed to write Java file: " + err.Error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("[JAVA-%s] Wrote code to file: %s", submission.ID, javaFilePath)
|
|
||||||
|
|
||||||
// Run Java code in container with performance optimizations
|
|
||||||
cmd := exec.Command("docker", "run", "--rm",
|
|
||||||
"--network=none", // No network access
|
|
||||||
"--memory=400m", // Memory limit
|
|
||||||
"--cpu-period=100000", // CPU quota period
|
|
||||||
"--cpu-quota=50000", // 50% CPU
|
|
||||||
"-v", tempDir+":/code", // Mount code directory
|
|
||||||
"eclipse-temurin:11-jdk-alpine", // JDK image with Alpine base (smaller and faster)
|
|
||||||
"sh", "-c", "cd /code && javac Main.java && java -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xverify:none -Xms64m -Xmx256m Main")
|
|
||||||
|
|
||||||
log.Printf("[JAVA-%s] Executing Java code with optimized settings", submission.ID)
|
|
||||||
output, err := s.executeWithTimeout(cmd, 20*time.Second, submission.ID)
|
|
||||||
|
|
||||||
elapsed := time.Since(startTime)
|
|
||||||
log.Printf("[JAVA-%s] Java execution completed in %v", submission.ID, elapsed)
|
|
||||||
|
|
||||||
s.updateSubmissionResult(submission, output, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// executeC runs C code in a container
|
|
||||||
func (s *ExecutionService) executeC(submission *model.CodeSubmission) {
|
|
||||||
log.Printf("[C-%s] Preparing C execution environment", submission.ID)
|
|
||||||
startTime := time.Now()
|
|
||||||
|
|
||||||
// Create temp directory for C files
|
|
||||||
tempDir, err := os.MkdirTemp("", "c-execution")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[C-%s] Failed to create temp directory: %v", submission.ID, err)
|
|
||||||
submission.Status = "failed"
|
|
||||||
submission.Output = "Failed to create temp directory: " + err.Error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tempDir)
|
|
||||||
log.Printf("[C-%s] Created temp directory: %s", submission.ID, tempDir)
|
|
||||||
|
|
||||||
// Write C code to file
|
|
||||||
cFilePath := filepath.Join(tempDir, "main.c")
|
|
||||||
if err := os.WriteFile(cFilePath, []byte(submission.Code), 0644); err != nil {
|
|
||||||
log.Printf("[C-%s] Failed to write C file: %v", submission.ID, err)
|
|
||||||
submission.Status = "failed"
|
|
||||||
submission.Output = "Failed to write C file: " + err.Error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("[C-%s] Wrote code to file: %s", submission.ID, cFilePath)
|
|
||||||
|
|
||||||
// Run C code in container
|
|
||||||
cmd := exec.Command("docker", "run", "--rm",
|
|
||||||
"--network=none", // No network access
|
|
||||||
"--memory=100m", // Memory limit
|
|
||||||
"--cpu-period=100000", // CPU quota period
|
|
||||||
"--cpu-quota=10000", // 10% CPU
|
|
||||||
"-v", tempDir+":/code", // Mount code directory
|
|
||||||
"gcc:latest", "bash", "-c", "cd /code && gcc -o main main.c && ./main")
|
|
||||||
|
|
||||||
log.Printf("[C-%s] Executing C code with timeout: 10s", submission.ID)
|
|
||||||
output, err := s.executeWithTimeout(cmd, 10*time.Second, submission.ID)
|
|
||||||
|
|
||||||
elapsed := time.Since(startTime)
|
|
||||||
log.Printf("[C-%s] C execution completed in %v", submission.ID, elapsed)
|
|
||||||
|
|
||||||
s.updateSubmissionResult(submission, output, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// executeCpp runs C++ code in a container
|
|
||||||
func (s *ExecutionService) executeCpp(submission *model.CodeSubmission) {
|
|
||||||
log.Printf("[CPP-%s] Preparing C++ execution environment", submission.ID)
|
|
||||||
startTime := time.Now()
|
|
||||||
|
|
||||||
// Create temp directory for C++ files
|
|
||||||
tempDir, err := os.MkdirTemp("", "cpp-execution")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[CPP-%s] Failed to create temp directory: %v", submission.ID, err)
|
|
||||||
submission.Status = "failed"
|
|
||||||
submission.Output = "Failed to create temp directory: " + err.Error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tempDir)
|
|
||||||
log.Printf("[CPP-%s] Created temp directory: %s", submission.ID, tempDir)
|
|
||||||
|
|
||||||
// Write C++ code to file
|
|
||||||
cppFilePath := filepath.Join(tempDir, "main.cpp")
|
|
||||||
if err := os.WriteFile(cppFilePath, []byte(submission.Code), 0644); err != nil {
|
|
||||||
log.Printf("[CPP-%s] Failed to write C++ file: %v", submission.ID, err)
|
|
||||||
submission.Status = "failed"
|
|
||||||
submission.Output = "Failed to write C++ file: " + err.Error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("[CPP-%s] Wrote code to file: %s", submission.ID, cppFilePath)
|
|
||||||
|
|
||||||
// Run C++ code in container
|
|
||||||
cmd := exec.Command("docker", "run", "--rm",
|
|
||||||
"--network=none", // No network access
|
|
||||||
"--memory=100m", // Memory limit
|
|
||||||
"--cpu-period=100000", // CPU quota period
|
|
||||||
"--cpu-quota=10000", // 10% CPU
|
|
||||||
"-v", tempDir+":/code", // Mount code directory
|
|
||||||
"gcc:latest", "bash", "-c", "cd /code && g++ -o main main.cpp && ./main")
|
|
||||||
|
|
||||||
log.Printf("[CPP-%s] Executing C++ code with timeout: 10s", submission.ID)
|
|
||||||
output, err := s.executeWithTimeout(cmd, 10*time.Second, submission.ID)
|
|
||||||
|
|
||||||
elapsed := time.Since(startTime)
|
|
||||||
log.Printf("[CPP-%s] C++ execution completed in %v", submission.ID, elapsed)
|
|
||||||
|
|
||||||
s.updateSubmissionResult(submission, output, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateSubmissionResult updates the submission with execution results
|
// updateSubmissionResult updates the submission with execution results
|
||||||
func (s *ExecutionService) updateSubmissionResult(submission *model.CodeSubmission, output []byte, err error) {
|
func (s *ExecutionService) updateSubmissionResult(submission *model.CodeSubmission, output []byte, err error) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
@@ -306,3 +475,11 @@ func (s *ExecutionService) updateSubmissionResult(submission *model.CodeSubmissi
|
|||||||
submission.ID, executionTime, totalTime, totalTime-executionTime)
|
submission.ID, executionTime, totalTime, totalTime-executionTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetQueueStats returns statistics about the job queue
|
||||||
|
func (s *ExecutionService) GetQueueStats() map[string]int {
|
||||||
|
stats := s.queue.QueueStats()
|
||||||
|
log.Printf("[QUEUE] Stats - Jobs in queue: %d, Running jobs: %d, Max workers: %d",
|
||||||
|
stats["queue_length"], stats["running_jobs"], stats["max_workers"])
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1
|
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1
|
||||||
Binary file not shown.
Reference in New Issue
Block a user