summaryrefslogblamecommitdiff
path: root/client/src/mill/client/Main.java
blob: 109a9a9d6dbc72fd6c907f8033dc4160c1e9449c (plain) (tree)
1
2
3
4
5
6
7
8
9
                    
 
                                

                 

                                   

                                     
                   
 
                   
                                                                                                       
                                                             
                                                          



                                                                           
                                                                           



                                          
                                          
                                                                                                            
         







                                                                                    


                                      
                     
                                                         
                                      
                        
 






                                                                     
                                                                      
                                                  


                                                    











                                                                                             
                                        




                                               
                                                                      
                                                    







                                                                  

                             













                                                               

                                                                    


                                                                     
                                      
                  





                                        

                                                         

                                                                 
                                                                     
 
                               



                                                                                  


                                                                                                               











































                                                                                       


                                       



                               
                                         
                              






















                                                       













                                                                                        
             



         
package mill.client;

import org.scalasbt.ipcsocket.*;

import java.io.*;
import java.net.Socket;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.util.*;

public class Main {
    static void initServer(String lockBase, boolean setJnaNoSys) throws IOException,URISyntaxException{
        ArrayList<String> selfJars = new ArrayList<String>();
        ClassLoader current = Main.class.getClassLoader();
        while(current != null){
            if (current instanceof java.net.URLClassLoader) {
                URL[] urls = ((java.net.URLClassLoader) current).getURLs();
                for (URL url: urls) {
                    selfJars.add(new File(url.toURI()).getCanonicalPath());
                }
            }
            current = current.getParent();
        }
        if (ClientServer.isJava9OrAbove) {
            selfJars.addAll(Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator)));
        }
        ArrayList<String> l = new java.util.ArrayList<String>();
        l.add("java");
        Properties props = System.getProperties();
        Iterator<String> keys = props.stringPropertyNames().iterator();
        while(keys.hasNext()){
            String k = keys.next();
            if (k.startsWith("MILL_")) l.add("-D" + k + "=" + props.getProperty(k));
        }
        if (setJnaNoSys) {
            l.add("-Djna.nosys=true");
        }
        l.add("-cp");
        l.add(String.join(File.pathSeparator, selfJars));
        l.add("mill.main.ServerMain");
        l.add(lockBase);

        new java.lang.ProcessBuilder()
                .command(l)
                .redirectOutput(new java.io.File(lockBase + "/logs"))
                .redirectError(new java.io.File(lockBase + "/logs"))
                .start();
    }
    public static void main(String[] args) throws Exception{
        boolean setJnaNoSys = System.getProperty("jna.nosys") == null;
        Map<String, String> env = System.getenv();
        if (setJnaNoSys) {
            System.setProperty("jna.nosys", "true");
        }
        int index = 0;
        while (index < 5) {
            index += 1;
            String lockBase = "out/mill-worker-" + index;
            new java.io.File(lockBase).mkdirs();
            RandomAccessFile lockFile = new RandomAccessFile(lockBase + "/clientLock", "rw");
            FileChannel channel = lockFile.getChannel();
            java.nio.channels.FileLock tryLock = channel.tryLock();
            if (tryLock == null) {
                lockFile.close();
                channel.close();
            } else {
                int exitCode = Main.run(
                        lockBase,
                        new Runnable() {
                            @Override
                            public void run() {
                                try{
                                    initServer(lockBase, setJnaNoSys);
                                }catch(Exception e){
                                    throw new RuntimeException(e);
                                }
                            }
                        },
                        Locks.files(lockBase),
                        System.in,
                        System.out,
                        System.err,
                        args,
                        env
                );
                System.exit(exitCode);
            }
        }
        throw new Exception("Reached max process limit: " + 5);
    }


    public static int run(String lockBase,
                          Runnable initServer,
                          Locks locks,
                          InputStream stdin,
                          OutputStream stdout,
                          OutputStream stderr,
                          String[] args,
                          Map<String, String> env) throws Exception{

        FileOutputStream f = new FileOutputStream(lockBase + "/run");
        ClientServer.writeArgs(System.console() != null, args, f);
        ClientServer.writeMap(env, f);
        f.close();

        boolean serverInit = false;
        if (locks.processLock.probe()) {
            serverInit = true;
            initServer.run();
        }
        while(locks.processLock.probe()) Thread.sleep(3);

        // Need to give sometime for Win32NamedPipeSocket to work
        // if the server is just initialized
        if (serverInit && ClientServer.isWindows) Thread.sleep(1000);

        Socket ioSocket = null;

        long retryStart = System.currentTimeMillis();
        while(ioSocket == null && System.currentTimeMillis() - retryStart < 1000){
            try{
                ioSocket = ClientServer.isWindows?
                        new Win32NamedPipeSocket(ClientServer.WIN32_PIPE_PREFIX + new File(lockBase).getName())
                        : new UnixDomainSocket(lockBase + "/io");
            }catch(Throwable e){
                Thread.sleep(1);
            }
        }
        if (ioSocket == null){
            throw new Exception("Failed to connect to server");
        }
        InputStream outErr = ioSocket.getInputStream();
        OutputStream in = ioSocket.getOutputStream();
        ClientOutputPumper outPump = new ClientOutputPumper(outErr, stdout, stderr);
        InputPumper inPump = new InputPumper(stdin, in, true);
        Thread outThread = new Thread(outPump);
        outThread.setDaemon(true);
        Thread inThread = new Thread(inPump);
        inThread.setDaemon(true);
        outThread.start();
        inThread.start();

        locks.serverLock.await();

        try{
            return Integer.parseInt(
                new BufferedReader(
                    new InputStreamReader(
                        new FileInputStream(lockBase + "/exitCode")
                    )
                ).readLine()
            );
        } catch(Throwable e){
            return 1;
        }
    }
}

class ClientOutputPumper implements Runnable{
    private InputStream src;
    private OutputStream dest1;
    private OutputStream dest2;
    public ClientOutputPumper(InputStream src, OutputStream dest1, OutputStream dest2){
        this.src = src;
        this.dest1 = dest1;
        this.dest2 = dest2;
    }

    public void run() {
        byte[] buffer = new byte[1024];
        int state = 0;
        boolean running = true;
        boolean first = true;
        while (running) {
            try {
                int n = src.read(buffer);
                first = false;
                if (n == -1) running = false;
                else {
                    int i = 0;
                    while (i < n) {
                        switch (state) {
                            case 0:
                                state = buffer[i] + 1;
                                break;
                            case 1:
                                dest1.write(buffer[i]);
                                state = 0;
                                break;
                            case 2:
                                dest2.write(buffer[i]);
                                state = 0;
                                break;
                        }

                        i += 1;
                    }
                    dest1.flush();
                    dest2.flush();
                }
            } catch (IOException e) {
                // Win32NamedPipeSocket input stream somehow doesn't return -1,
                // instead it throws an IOException whose message contains "ReadFile()".
                // However, if it throws an IOException before ever reading some bytes,
                // it could not connect to the server, so exit.
                if (ClientServer.isWindows && e.getMessage().contains("ReadFile()")) {
                    if (first) {
                        System.err.println("Failed to connect to server");
                        System.exit(1);
                    } else running = false;
                } else {
                    e.printStackTrace();
                    System.exit(1);
                }
            }
        }
    }

}