docs: private void executeSlave(final ForkedJvmInfo slave, final EventBus aggregatedBus)
      throws Exception
    {
      final String uniqueSeed = new SimpleDateFormat("yyyyMMdd_HHmmss_SSS", Locale.ROOT).format(new Date());
  
      final Path classNamesFile = tempFile(uniqueSeed, "junit4-J" + slave.id, ".suites", getTempDir());
      temporaryFiles.add(classNamesFile);
  
      final Path classNamesDynamic = tempFile(uniqueSeed, "junit4-J" + slave.id, ".dynamic-suites", getTempDir());
      final Path streamsBufferFile = tempFile(uniqueSeed, "junit4-J" + slave.id, ".spill", getTempDir());
  
      // Dump all test class names to a temporary file.
      String testClassPerLine = Joiner.on("\n").join(slave.testSuites);
      log("Test class names:\n" + testClassPerLine, Project.MSG_VERBOSE);
      Files.write(classNamesFile, testClassPerLine.getBytes(StandardCharsets.UTF_8));
  
      // Prepare command line for java execution.
      CommandlineJava commandline;
      commandline = (CommandlineJava) getCommandline().clone();
      commandline.createClasspath(getProject()).add(addSlaveClasspath());
      commandline.setClassname(SlaveMainSafe.class.getName());
      if (slave.slaves == 1) {
        commandline.createArgument().setValue(SlaveMain.OPTION_FREQUENT_FLUSH);
      }
  
      // Set up full output files.
      Path sysoutFile = tempFile(uniqueSeed,
          "junit4-J" + slave.id, ".sysout", getTempDir());
      Path syserrFile = tempFile(uniqueSeed,
          "junit4-J" + slave.id, ".syserr", getTempDir());
  
      // Set up communication channel.
      Path eventFile = tempFile(uniqueSeed, "junit4-J" + slave.id, ".events", getTempDir());
      temporaryFiles.add(eventFile);
      commandline.createArgument().setValue(SlaveMain.OPTION_EVENTSFILE);
      commandline.createArgument().setFile(eventFile.toFile());
      
      if (sysouts) {
        commandline.createArgument().setValue(SlaveMain.OPTION_SYSOUTS);
      }
      
      if (debugStream) {
        commandline.createArgument().setValue(SlaveMain.OPTION_DEBUGSTREAM);
      }
  
      TailInputStream eventStream = new TailInputStream(eventFile);
  
      // Process user-defined RunListener classes.
      if (!runListeners.isEmpty()) {
        String classNames = runListeners.stream().map(x -> x.getClassName()).collect(Collectors.joining(","));
  
        commandline.createArgument().setValue(SlaveMain.OPTION_RUN_LISTENERS);
        commandline.createArgument().setValue(classNames);
      }
  
      // Set up input suites file.
      commandline.createArgument().setValue("@" + classNamesFile.toAbsolutePath().normalize());
  
      // May or may not use dynamic load balancing, but if == 0 then we're for sure
      // not using it.
      if (dynamicAssignmentRatio > 0) {
        commandline.createArgument().setValue(SlaveMain.OPTION_STDIN);
      }
  
      final EventBus eventBus = new EventBus("slave-" + slave.id);
      final DiagnosticsListener diagnosticsListener = new DiagnosticsListener(slave, this);
      eventBus.register(diagnosticsListener);
      eventBus.register(new AggregatingListener(aggregatedBus, slave));
  
      final AtomicReference<Charset> clientCharset = new AtomicReference<Charset>();
      final AtomicBoolean clientWithLimitedCharset = new AtomicBoolean(); 
      final PrintWriter w = new PrintWriter(Files.newBufferedWriter(classNamesDynamic, StandardCharsets.UTF_8));
      eventBus.register(new Object() {
        @Subscribe
        public void onIdleSlave(final SlaveIdle idleSlave) {
          aggregatedBus.post(new SlaveIdle() {
            @Override
            public void finished() {
              idleSlave.finished();
            }
  
            @Override
            public void newSuite(String suiteName) {
              if (!clientCharset.get().newEncoder().canEncode(suiteName)) {
                clientWithLimitedCharset.set(true);
                log("Forked JVM J" + slave.id + " skipped suite (cannot encode suite name in charset " +
                    clientCharset.get() + "): " + suiteName, Project.MSG_WARN);
                return;
              }
  
              log("Forked JVM J" + slave.id + " stole suite: " + suiteName, Project.MSG_VERBOSE);
              w.println(suiteName);
              w.flush();
              idleSlave.newSuite(suiteName);
            }
          });
        }
  
        @Subscribe
        public void onBootstrap(final BootstrapEvent e) {
          Charset cs = Charset.forName(((BootstrapEvent) e).getDefaultCharsetName());
          clientCharset.set(cs);
  
          slave.start = System.currentTimeMillis();
          slave.setBootstrapEvent(e);
          aggregatedBus.post(new ChildBootstrap(slave));
        }
  
        @Subscribe
        public void receiveQuit(QuitEvent e) {
          slave.end = System.currentTimeMillis();
        }      
      });
  
      Closer closer = Closer.create();
      closer.register(eventStream);
      closer.register(w);
      try {
        OutputStream sysout = closer.register(new BufferedOutputStream(Files.newOutputStream(sysoutFile)));
        OutputStream syserr = closer.register(new BufferedOutputStream(Files.newOutputStream(syserrFile)));
        RandomAccessFile streamsBuffer = closer.register(new RandomAccessFile(streamsBufferFile.toFile(), "rw"));
  
        Execute execute = forkProcess(slave, eventBus, commandline, eventStream, sysout, syserr, streamsBuffer);
        log("Forked JVM J" + slave.id + " finished with exit code: " + execute.getExitValue(), Project.MSG_DEBUG);
  
        if (execute.isFailure()) {
          final int exitStatus = execute.getExitValue();
          switch (exitStatus) {
            case SlaveMain.ERR_NO_JUNIT:
              throw new BuildException("Forked JVM's classpath must include a junit4 JAR.");
            case SlaveMain.ERR_OLD_JUNIT:
              throw new BuildException("Forked JVM's classpath must use JUnit 4.10 or newer.");
            default:
              Closeables.close(sysout, false);
              Closeables.close(syserr, false);
  
              StringBuilder message = new StringBuilder();
              if (exitStatus == SlaveMain.ERR_OOM) {
                message.append("Forked JVM ran out of memory.");
              } else {
                message.append("Forked process returned with error code: ").append(exitStatus).append(".");
              }
  
              if (Files.size(sysoutFile) > 0 || Files.size(syserrFile) > 0) {
                if (exitStatus != SlaveMain.ERR_OOM) {
                  message.append(" Very likely a JVM crash. ");
                }
  
                if (jvmOutputAction.contains(JvmOutputAction.PIPE)) {
                  message.append(" Process output piped in logs above.");
                } else if (!jvmOutputAction.contains(JvmOutputAction.IGNORE)) {
                  if (Files.size(sysoutFile) > 0) {
                    message.append(" See process stdout at: " + sysoutFile.toAbsolutePath());
                  }
                  if (Files.size(syserrFile) > 0) {
                    message.append(" See process stderr at: " + syserrFile.toAbsolutePath());
                  }
                }
              }
              throw new BuildException(message.toString());
          }
        }
      } catch (Throwable t) {
        throw closer.rethrow(t);
      } finally {
        try {
          closer.close();
        } finally {
          com.google.common.io.Files.asByteSource(classNamesDynamic.toFile())
            .copyTo(com.google.common.io.Files.asByteSink(classNamesFile.toFile(), FileWriteMode.APPEND));
          Files.delete(classNamesDynamic);
          Files.delete(streamsBufferFile);
  
          // Check sysout/syserr lengths.
          checkJvmOutput(aggregatedBus, sysoutFile, slave, "stdout");
          checkJvmOutput(aggregatedBus, syserrFile, slave, "stderr");        
        }
      }
  
      if (!diagnosticsListener.quitReceived()) {
        throw new BuildException("Quit event not received from the forked process? This may indicate JVM crash or runner bugs.");
      }
  
      if (clientWithLimitedCharset.get() && dynamicAssignmentRatio > 0) {
        throw new BuildException("Forked JVM J" + slave.id + " was not be able to decode class names when using" +
            " charset: " + clientCharset + ". Do not use " +
            "dynamic suite balancing to work around this problem (-DdynamicAssignmentRatio=0).");
      }
    }