/* * Sandbox.java * * Copyright (C) 2015 Pixelgaffer * * This work is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; either version 2 of the License, or any later * version. * * This work is distributed in the hope that it will be useful, but without * any warranty; without even the implied warranty of merchantability or * fitness for a particular purpose. See version 2 and version 3 of the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.pixelgaffer.turnierserver.worker; import static org.pixelgaffer.turnierserver.networking.messages.SandboxCommand.CPU_TIME; import static org.pixelgaffer.turnierserver.networking.messages.SandboxCommand.KILL_AI; import static org.pixelgaffer.turnierserver.networking.messages.SandboxCommand.RUN_AI; import static org.pixelgaffer.turnierserver.networking.messages.SandboxCommand.TERM_AI; import static org.pixelgaffer.turnierserver.networking.messages.SandboxMessage.FINISHED_AI; import static org.pixelgaffer.turnierserver.networking.messages.SandboxMessage.KILLED_AI; import static org.pixelgaffer.turnierserver.networking.messages.SandboxMessage.STARTED_AI; import static org.pixelgaffer.turnierserver.networking.messages.SandboxMessage.TERMINATED_AI; import static org.pixelgaffer.turnierserver.networking.messages.WorkerConnectionType.SANDBOX; import java.io.IOException; import java.util.Set; import java.util.UUID; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import org.pixelgaffer.turnierserver.Airbrake; import org.pixelgaffer.turnierserver.networking.messages.SandboxCommand; import org.pixelgaffer.turnierserver.networking.messages.SandboxMessage; import org.pixelgaffer.turnierserver.networking.messages.WorkerInfo.SandboxInfo; import org.pixelgaffer.turnierserver.worker.server.WorkerConnectionHandler; import lombok.Getter; import lombok.NonNull; import lombok.ToString; /** * Repräsentiert eine Sandbox. */ @ToString(exclude = { "connection", "semaphore", "cpuTimeLock" }) public class Sandbox { @Getter private SandboxInfo sandboxInfo = new SandboxInfo(); @Getter private long lastCpuTime; private Semaphore semaphore = new Semaphore(1, true); private Object cpuTimeLock = new Object(); public void updateCpuTime () { Thread sendJob = new Thread( () -> { try { while (semaphore.tryAcquire(500, TimeUnit.MICROSECONDS)) { semaphore.release(); } sendJob(new SandboxCommand(CPU_TIME, -1, -1, -1, "", null, -1)); } catch (Exception e) { Airbrake.log(e).printStackTrace(); } }); // WorkerMain.getLogger().debug( // "Gehe in Synchronized in update " + currentJob + " in thread " + Thread.currentThread()); synchronized (cpuTimeLock) { try { // WorkerMain.getLogger().debug( // "Warte auf notify in uuid " + currentJob + " in thread " + Thread.currentThread()); sendJob.start(); semaphore.acquire(); cpuTimeLock.wait(); semaphore.release(); // WorkerMain.getLogger().debug( // "Wurde notified in uuid " + currentJob + " in thread " + Thread.currentThread()); } catch (InterruptedException e) { e.printStackTrace(); } } // WorkerMain.getLogger().debug( // "Gehe aus Synchronized in update " + currentJob + " in thread " + Thread.currentThread()); } public long getCpuTimeDiff () { long oldTime = lastCpuTime; updateCpuTime(); return Math.max(lastCpuTime - oldTime, 0); } private void setBusy (boolean busy) { if (isBusy() != busy) { sandboxInfo.setBusy(busy); try { WorkerMain.notifyInfoUpdated(); } catch (IOException e) { WorkerMain.getLogger().critical("Failed to notify Backend that the Worker changed: " + e); } } } public boolean isBusy () { return sandboxInfo.isBusy(); } public void setLangs (@NonNull Set<String> langs) { if (!langs.equals(getLangs())) { sandboxInfo.setLangs(langs); try { WorkerMain.notifyInfoUpdated(); } catch (IOException e) { WorkerMain.getLogger().critical("Failed to notify Backend that the Worker changed: " + e); } } } public Set<String> getLangs () { return sandboxInfo.getLangs(); } /** Die UUID des aktuell in der Sandbox ausgeführten Jobs. */ private UUID currentJob; /** Die Connection von der Sandbox zum Worker. */ @Getter private WorkerConnectionHandler connection; public Sandbox (WorkerConnectionHandler connectionHandler) { if (connectionHandler.getType().getType() != SANDBOX) throw new IllegalArgumentException(); connection = connectionHandler; } /** * Schickt den Job an die Sandbox. Dabei wird vorrausgesetzt, dass die * Sandbox nicht beschäftigt ist. Gibt bei Erfolg true zurück, ansonsten * false. */ public synchronized boolean sendJob (SandboxCommand job) throws IOException { WorkerMain.getLogger().debug("Sende " + job); if (job.getCommand() == RUN_AI) { if (isBusy()) return false; setBusy(true); } else if ((job.getCommand() == KILL_AI) || (job.getCommand() == TERM_AI)) setBusy(false); currentJob = job.getUuid(); connection.sendJob(job); return true; } /** * Empfängt die Antwort der Sandbox. */ public synchronized void sandboxAnswer (SandboxMessage answer) { switch (answer.getEvent()) { case TERMINATED_AI: case KILLED_AI: case FINISHED_AI: try { Sandboxes.releaseIsolateBoxid(answer.getUuid()); } catch (Exception e) { WorkerMain.getLogger().critical("Fehler beim releasen der isolate boxid von " + answer.getUuid()); Airbrake.log(e).printStackTrace(); } try { WorkerMain.getLogger().info("Die KI " + answer.getUuid() + " hat sich mit " + answer.getEvent() + " beendet"); WorkerMain.getBackendClient().sendSandboxMessage(answer); } catch (IOException e) { WorkerMain.getLogger().critical("Fehler beim notifien des Backends (" + answer + "): " + e); Airbrake.log(e).printStackTrace(); } setBusy(false); break; case STARTED_AI: WorkerMain.getLogger().todo("Hier sollte ich mir überlegen ob ich iwas notifien soll"); setBusy(true); break; case CPU_TIME: lastCpuTime = answer.getCpuTime(); // WorkerMain.getLogger().debug( // "Gehe in Synchronized in " + currentJob + " in thread " + Thread.currentThread()); synchronized (cpuTimeLock) { // WorkerMain.getLogger().debug("Notify " + currentJob + " in thread " + Thread.currentThread()); cpuTimeLock.notifyAll(); // WorkerMain.getLogger().debug("Notified " + currentJob + " in thread " + Thread.currentThread()); } // WorkerMain.getLogger().debug( // "Gehe aus Synchronized in " + currentJob + " in thread " + Thread.currentThread()); break; default: WorkerMain.getLogger().critical("Unknown event received:" + answer); break; } } /** * Wird aufgerufen, wenn sich die Sandbox disconnected hat. */ public void disconnected () { sandboxAnswer(new SandboxMessage(TERMINATED_AI, currentJob)); } }