Uploaded image for project: 'Commons Exec'
  1. Commons Exec
  2. EXEC-63

Race condition in DefaultExecutor#execute(cmd, handler)

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Open
    • Major
    • Resolution: Unresolved
    • 1.1
    • None
    • Windows 7/64 bit, JDK 1.6.0_27

    Description

      DefaultExecutor#execute(CommandLine, ExecuteResultHandler) can, and usually does, return before the target process has actually started. This can result in a race condition where several asynchronous processes are coupled by PipedInputStream/PipedOutputStream objects.

      The following example shows the issue:

      Borken.java
      import java.io.*;
      import org.apache.commons.exec.*;
      
      public class Borken {
        public static int pipe(OutputStream out, OutputStream err, InputStream in, CommandLine cmd0, CommandLine cmd1)
            throws IOException {
          PipedInputStream pipeIn = new PipedInputStream();
          PipedOutputStream pipeOut = new PipedOutputStream(pipeIn);
          DefaultExecutor exec0 = new DefaultExecutor();
          exec0.setStreamHandler(new PumpStreamHandler(pipeOut, null, in));
          exec0.execute(cmd0, new DefaultExecuteResultHandler());
          
          // If the following line is commented, deadlock occurs
          //try { Thread.sleep(100); } catch (InterruptedException e) { }
      
          DefaultExecutor exec1 = new DefaultExecutor();
          exec1.setStreamHandler(new PumpStreamHandler(out, err, pipeIn));
          return exec1.execute(cmd1);
        }
        
        public static void main(String... args) {
          CommandLine cmd0 = new CommandLine("cmd").addArgument("/c").addArgument("dir");
          //CommandLine cmd0 = new CommandLine("ls").addArgument("-l");
          CommandLine cmd1 = new CommandLine("sort");
          ByteArrayOutputStream out = new ByteArrayOutputStream();
          ByteArrayOutputStream err = new ByteArrayOutputStream();
          ByteArrayInputStream in = new ByteArrayInputStream(new byte[0]);
          try {
            int result = pipe(out, err, in, cmd0, cmd1);
            System.out.format("Result code: %d%n", result);
            System.out.format("Out: %s%n", out.toString());
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
      

      One possible solution is to pass in a semaphore object into DefaultExecutor#executeInternal which is notified once the process is started. The execute(CommandLine, Map, ExecuteResultHandler) method can then wait on this before returning.

      Attachments

        1. EXEC-63_DefaultExecutor.patch
          4 kB
          Michael Vorburger

        Activity

          People

            Unassigned Unassigned
            msandiford Martin Sandiford
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:

              Time Tracking

                Estimated:
                Original Estimate - 24h
                24h
                Remaining:
                Remaining Estimate - 24h
                24h
                Logged:
                Time Spent - Not Specified
                Not Specified