diff options
Diffstat (limited to 'jack-server/src/com/android/jack/server/Server.java')
| -rw-r--r-- | jack-server/src/com/android/jack/server/Server.java | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/jack-server/src/com/android/jack/server/Server.java b/jack-server/src/com/android/jack/server/Server.java new file mode 100644 index 00000000..8ab31531 --- /dev/null +++ b/jack-server/src/com/android/jack/server/Server.java @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.jack.server; + +import com.android.sched.util.config.cli.TokenIterator; +import com.android.sched.util.file.AbstractStreamFile; +import com.android.sched.util.file.OutputStreamFile; +import com.android.sched.util.location.FileLocation; +import com.android.sched.util.location.NoLocation; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintStream; +import java.util.Random; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +/** + * Server controlling the number of Jack compilations that are executed simultaneously. + */ +public class Server { + @Nonnull + private static ServerTask serviceTest = new ServerTask() { + @Nonnull + private final Random rnd = new Random(); + + @Override + public int run(@Nonnull PrintStream out, @Nonnull PrintStream err, @Nonnull File workingDir, + @Nonnull TokenIterator args) { + String cmd = null; + try { + args.hasNext(); + cmd = args.next(); + } catch (Throwable e) { + e.printStackTrace(); + } + + out.println("Pre-test stdout for '" + workingDir.getPath() + "'"); + err.println("Pre-test stderr for '" + cmd + "'"); + + try { + Thread.sleep(rnd.nextInt(3000)); + } catch (InterruptedException e) { + // Doesn't matter + } + + out.println("Post-test stdout for '" + workingDir.getPath() + "'"); + err.println("Post-test stderr for '" + cmd + "'"); + + return rnd.nextInt(30); + } + }; + + @Nonnull + private static ServerTask service = new ServerTaskInsideVm(); + + @Nonnull + private static Logger logger = Logger.getLogger(Server.class.getSimpleName()); + + private static final int CMD_IDX_CMD = 0; + private static final int CMD_IDX_OUT = 1; + private static final int CMD_IDX_ERR = 2; + private static final int CMD_IDX_EXIT = 3; + private static final int CMD_IDX_CLI = 4; + private static final int CMD_IDX_END = 5; + + private static final int CLI_IDX_MAX = 0; + private static final int CLI_IDX_TIEMOUT = 1; + private static final int CLI_IDX_FIFO = 2; + private static final int CLI_IDX_LOCK = 3; + private static final int CLI_IDX_END = 4; + + @CheckForNull + private static File fifo; + @CheckForNull + private static File lock; + @CheckForNull + private static BufferedReader in; + + private static int timeout; + + @Nonnull + private static AtomicInteger nbMax = new AtomicInteger(0); + @Nonnull + private static AtomicLong nbCurrent = new AtomicLong(0); + + public static void main(String[] args) throws InterruptedException { + if (args.length != CLI_IDX_END) { + logger.log(Level.SEVERE, "Usage: <max-compile> <timeout-s> <path-fifo> <path-lock>"); + abort(); + } + + int nbInstance = 0; + try { + nbInstance = Integer.parseInt(args[CLI_IDX_MAX]); + } catch (NumberFormatException e) { + logger.log(Level.SEVERE, "Cannot parse instance count '" + args[CLI_IDX_MAX] + "'"); + abort(); + } + + try { + timeout = Integer.parseInt(args[CLI_IDX_TIEMOUT]) * 1000; + } catch (NumberFormatException e) { + logger.log(Level.SEVERE, "Cannot parse timeout '" + args[CLI_IDX_TIEMOUT] + "'"); + abort(); + } + + fifo = new File(args[CLI_IDX_FIFO]); + lock = new File(args[CLI_IDX_LOCK]); + + + try { + AbstractStreamFile.check(fifo, new FileLocation(fifo)); + } catch (IOException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + abort(); + } + + Runtime.getRuntime().addShutdownHook(new Thread(){ + @Override + public void run() { + cancelTimer(); + shutdownFifo(); + + if (!lock.delete()) { + logger.log(Level.SEVERE, "Can not remove lock file '" + lock.getPath() + "'"); + } + } + }); + + startFifo(); + startTimer(); + try { + in = new BufferedReader(new FileReader(fifo)); + } catch (FileNotFoundException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + abort(); + } + + assert fifo != null; + logger.log(Level.INFO, "Starting server on '" + fifo.getPath() + "'"); + ExecutorService executor = Executors.newFixedThreadPool(nbInstance); + for (int i = 0; i < nbInstance; i++) { + logger.log(Level.FINE, "Launching task #" + i); + executor.execute(new Task()); + } + + executor.shutdown(); + executor.awaitTermination(1, TimeUnit.HOURS); + + logger.log(Level.INFO, "Shutdown server"); + logger.log(Level.INFO, "# service runs " + nbCurrent.get()); + } + + /** + * {@link Runnable} task launched by the server that will be in charge to call {@link ServerTask} + * implementation that will launch a Jack compilation either in the same VM, either in a spawn VM. + */ + public static class Task implements Runnable { + @Override + public void run() { + while (true) { + String line; + + try { + line = getLine(); + logger.log(Level.FINE, "Read command '" + line + "'"); + } catch (IOException e) { + logger.log(Level.FINE, "Shutdown task"); + return; + } + + String[] command = line.split(" "); + + if (command[CMD_IDX_CMD].equals("=")) { + continue; + } + + if (command[CMD_IDX_CMD].equals("-")) { + cancelTimer(); + shutdownFifo(); + continue; + } + + if (!command[CMD_IDX_CMD].equals("+")) { + logger.log(Level.SEVERE, "Command error '" + line + "'"); + continue; + } + + PrintStream out = null; + PrintStream err = null; + PrintStream exit = null; + + try { + if (command.length != CMD_IDX_END) { + logger.log(Level.SEVERE, "Command format error '" + line + "'"); + continue; + } + + logger.log(Level.FINE, "Open standard output '" + command[CMD_IDX_OUT] + "'"); + try { + out = new OutputStreamFile(command[CMD_IDX_OUT]).getPrintStream(); + } catch (IOException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + continue; + } + + logger.log(Level.FINE, "Open standard error '" + command[CMD_IDX_ERR] + "'"); + try { + err = new OutputStreamFile(command[CMD_IDX_ERR]).getPrintStream(); + } catch (IOException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + continue; + } + + logger.log(Level.FINE, "Open exit fifo '" + command[CMD_IDX_EXIT] + "'"); + try { + exit = new OutputStreamFile(command[CMD_IDX_EXIT]).getPrintStream(); + } catch (IOException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + continue; + } + + logger.log(Level.FINE, "Parse command line"); + TokenIterator args = new TokenIterator(new NoLocation(), "@" + command[CMD_IDX_CLI]); + args.allowFileReferenceInFile(); + if (!args.hasNext()) { + logger.log(Level.SEVERE, "Cli format error"); + continue; + } + + String workingDir; + try { + workingDir = args.next(); + } catch (IOException e) { + logger.log(Level.SEVERE, "Cli format error"); + continue; + } + + if (nbMax.getAndIncrement() == 0) { + cancelTimer(); + } + + int code = -1; + try { + logger.log(Level.FINE, "Run service"); + nbCurrent.incrementAndGet(); + code = service.run(out, err, new File(workingDir), args); + } finally { + if (nbMax.decrementAndGet() == 0) { + startTimer(); + } + + logger.log(Level.FINE, "Write exit code '" + code + "'"); + assert exit != null; + exit.println(code); + } + } finally { + if (out != null) { + out.close(); + } else { + unblock(command[CMD_IDX_OUT]); + } + + if (err != null) { + err.close(); + } else { + unblock(command[CMD_IDX_ERR]); + } + + if (exit != null) { + exit.close(); + } else { + unblock(command[CMD_IDX_EXIT]); + } + } + } + } + } + + @Nonnull + private static Object lockRead = new Object(); + + private static volatile boolean stop = false; + + @Nonnull + public static String getLine() throws IOException { + synchronized (lockRead) { + if (in == null) { + throw new IOException(); + } + + assert in != null; + String str = in.readLine(); + while (str == null) { + try { + in.close(); + } catch (IOException e1) { + // Best effort + } + in = null; + + if (stop) { + throw new IOException(); + } + + in = new BufferedReader(new FileReader(fifo)); + assert in != null; + str = in.readLine(); + } + + return str; + } + } + + private static void startFifo() { + logger.log(Level.FINE, "Start FIFO"); + } + + private static void shutdownFifo() { + logger.log(Level.FINE, "Shutdown FIFO"); + + stop = true; + + Unblocker unblocker = new Unblocker(); + unblocker.setName("Unblocker"); + unblocker.setDaemon(true); + unblocker.start(); + } + + private static class Unblocker extends Thread { + @Override + public void run() { + PrintStream out = null; + + while (true) { + try { + out = new PrintStream(fifo); + } catch (FileNotFoundException e) { + // Best effort + } + + if (out != null) { + out.close(); + } + + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + // Best effort here + } + } + } + } + + private static void abort() { + System.exit(1); + } + + @CheckForNull + private static Timer timer; + + @Nonnull + private static Object lockTimer = new Object(); + + private static void startTimer() { + synchronized (lockTimer) { + if (timer != null) { + cancelTimer(); + } + + logger.log(Level.FINE, "Start timer"); + + timer = new Timer("jack-server-timeout"); + assert timer != null; + timer.schedule(new TimerTask() { + @Override + public void run() { + shutdownFifo(); + cancelTimer(); + } + }, timeout); + } + } + + private static void cancelTimer() { + synchronized (lockTimer) { + if (timer != null) { + logger.log(Level.FINE, "Cancel timer"); + + timer.cancel(); + timer.purge(); + timer = null; + } + } + } + + private static void unblock(@Nonnull String name) { + logger.log(Level.FINE, "Trying to unblock '" + name + "'"); + PrintStream out = null; + try { + out = new OutputStreamFile(name).getPrintStream(); + } catch (IOException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + + if (out != null) { + out.close(); + } + } +} |
