package cz.cuni.mff.d3s.been.hostruntime; import static cz.cuni.mff.d3s.been.core.task.TaskExclusivity.*; import java.io.IOException; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import cz.cuni.mff.d3s.been.cluster.context.ClusterContext; import cz.cuni.mff.d3s.been.core.ri.MonitorSample; import cz.cuni.mff.d3s.been.core.ri.RuntimeInfo; import cz.cuni.mff.d3s.been.core.ri.RuntimeInfos; import cz.cuni.mff.d3s.been.core.task.TaskExclusivity; import cz.cuni.mff.d3s.been.debugassistant.DebugAssistant; import cz.cuni.mff.d3s.been.hostruntime.task.TaskHandle; import cz.cuni.mff.d3s.been.hostruntime.task.TaskProcess; /** * * Keeps track of the current state of tasks running on a Host Runtime including * exclusivity * * @author Martin Sixta */ final class ProcessManagerContext { /** logging */ private static Logger log = LoggerFactory.getLogger(ProcessManagerContext.class); /** connection to the cluster */ private final ClusterContext clusterContext; /** current Host Runtime info */ private final RuntimeInfo hostInfo; /** Maps task IDs to its Process */ private final Map<String, TaskProcess> runningTasks = Collections.synchronizedMap(new HashMap<String, TaskProcess>()); /** current exclusivity level */ private volatile TaskExclusivity currentExclusivity = NON_EXCLUSIVE; /** current exclusive ID (task or context) */ private volatile String currentExclusiveId = null; /** set of reserved / accepted tasks */ Set<String> acceptedTasks = new HashSet<>(); /** * Creates new ProcessManagerContext * * @param clusterContext * connection to the cluster * @param hostInfo * Host Runtime info */ ProcessManagerContext(ClusterContext clusterContext, RuntimeInfo hostInfo) { this.clusterContext = clusterContext; this.hostInfo = hostInfo; } /** * * Tries to accept a task. * * If successful all necessary data structures (cluster wide) will be updated. * * @param taskHandle * task handle * @throws IllegalStateException * if a task cannot be accepted to run on this Host Runtime */ synchronized void tryAcceptTask(TaskHandle taskHandle) throws IllegalStateException { tryCheckLoad(); TaskExclusivity prevExclusivity = currentExclusivity; String prevExclusiveId = currentExclusiveId; boolean canAccept = tryChangeExclusivity(taskHandle); if (canAccept) { try { taskHandle.setAccepted(); acceptedTasks.add(taskHandle.getTaskId()); } catch (IllegalStateException e) { // reset exclusivity setExclusivity(prevExclusivity, prevExclusiveId); throw e; } } else { throw new IllegalStateException("Exclusivity cannot be satisfied"); } updateHostInfo(); } /** * Checks the current state of the runtime for overload conditions. * * Memory load Maximum tasks * * @throws IllegalStateException * if a limit is reached */ private void tryCheckLoad() throws IllegalStateException { if (RuntimeInfos.isMaxTasksReached(hostInfo)) { throw new IllegalStateException("Maximum number of tasks reached"); } if (RuntimeInfos.isMemoryThresholdReached(hostInfo)) { throw new IllegalStateException("Memory threshold reached"); } } /** * * Adds a running task. * * Updates all necessary data structures * * @param id * ID of t * @param process * process representing the task */ synchronized void addTask(String id, TaskProcess process) { assert (acceptedTasks.contains(id)); runningTasks.put(id, process); } /** * * Removes a task. * * Updates all necessary data structures * * @param taskHandle * task handle */ synchronized void removeTask(TaskHandle taskHandle) { runningTasks.remove(taskHandle.getTaskId()); acceptedTasks.remove(taskHandle.getTaskId()); if (getTasksCount() == 0) { setExclusivity(NON_EXCLUSIVE, null); } updateHostInfo(); DebugAssistant debugAssistant = new DebugAssistant(clusterContext); debugAssistant.removeSuspendedTask(taskHandle.getTaskId()); } /** * Returns current count of accepted tasks. * * Be aware that an accepted task does not have to have it's corresponding * {@link TaskProcess} created yet. * * @return number of accepted tasks of the Host Runtime */ int getTasksCount() { return acceptedTasks.size(); } /** * Kills all running tasks */ void killRunningTasks() { try { for (TaskProcess process : runningTasks.values()) { log.debug("Killing task process {}", process); process.kill(); } while (!runningTasks.isEmpty()) { Thread.sleep(500); } } catch (InterruptedException e) { // give up } } /** * Kills a running task * * @param id * ID of the task to kill */ public void killTask(String id) { TaskProcess taskProcess = runningTasks.get(id); if (taskProcess != null) { taskProcess.kill(); } } /** * Updates load information of the Host Runtime * * @param sample * monitoring sample */ public synchronized void updateMonitoringSample(MonitorSample sample) { hostInfo.setMonitorSample(sample); updateHostInfo(); } /** * Updates information about tasks directories */ public synchronized void updateTaskDirs() { List<String> taskDirs = hostInfo.getTaskDirs(); taskDirs.clear(); try { final Path workingDirectory = Paths.get(hostInfo.getTasksWorkingDirectory()); for (Path item : Files.newDirectoryStream(workingDirectory)) { taskDirs.add(item.toAbsolutePath().toString()); } } catch (IOException | InvalidPathException e) { log.error("Cannot list working directory of tasks", e); } updateHostInfo(); } /** * Updates Host Runtime information in the cluster */ private void updateHostInfo() { hostInfo.setExclusivity(currentExclusivity.toString()); hostInfo.setExclusiveId(currentExclusiveId); hostInfo.setTaskCount(getTasksCount()); clusterContext.getRuntimes().storeRuntimeInfo(hostInfo); } /** * Sets current exclusivity. * * @param exclusivity * Host Runtime exclusivity * @param exclusiveId * ID associated with the exclusivity */ private void setExclusivity(TaskExclusivity exclusivity, String exclusiveId) { currentExclusivity = exclusivity; currentExclusiveId = exclusiveId; } /** * Sets current exclusivity from a task handle. * * @param taskHandle * task handle */ private void setExclusivity(TaskHandle taskHandle) { TaskExclusivity exclusivity = taskHandle.getExclusivity(); if (exclusivity == CONTEXT_EXCLUSIVE) { setExclusivity(exclusivity, taskHandle.getContextId()); } else if (exclusivity == EXCLUSIVE) { setExclusivity(exclusivity, taskHandle.getTaskId()); } } /** * * Tries to change current exclusivity. * * @param taskHandle * task handle * @return true if current exclusivity was changed, false if the exclusivity * cannot be changed */ private boolean tryChangeExclusivity(TaskHandle taskHandle) { TaskExclusivity exclusivity = taskHandle.getExclusivity(); switch (currentExclusivity) { case NON_EXCLUSIVE: boolean isExclusive = (exclusivity != NON_EXCLUSIVE); boolean isFree = (getTasksCount() == 0); if (isExclusive && isFree) { setExclusivity(taskHandle); return true; } else if (isExclusive) { return false; // there are running tasks } else { setExclusivity(taskHandle); return true; } case CONTEXT_EXCLUSIVE: return taskHandle.getContextId().equals(currentExclusiveId); case EXCLUSIVE: return false; default: // should not happen, make the compiler happy log.error("Unimplemented case statement!"); return false; } } }