@Override
  public void run() {
    final LowMemoryWatcherManager memWatcher = new LowMemoryWatcherManager(SharedThreadPool.getInstance());
    Throwable error = null;
    final Ref<Boolean> hasErrors = new Ref<>(false);
    final Ref<Boolean> doneSomething = new Ref<>(false);
    try {
      ProfilingHelper profilingHelper;

      try {
        Utils.ProfilingMode profilingMode = Utils.getProfilingMode();

        switch (profilingMode) {
          case NONE:
            profilingHelper = null;
            break;
          case YOURKIT_SAMPLING:
            profilingHelper = new ProfilingHelper();
            profilingHelper.startSamplingProfiling();
            break;
          case YOURKIT_TRACING:
            profilingHelper = new ProfilingHelper();
            profilingHelper.startTracingProfiling();
            break;
          default:
            throw new IllegalArgumentException("Unsupported profiling mode: " + profilingMode);
        }
      }
      catch (Throwable t) {
        LOG.warn("Unable to start build process profiling: " + t.getMessage(), t);
        //noinspection CallToPrintStackTrace
        t.printStackTrace();
        profilingHelper = null;
      }

      myCacheLoadManager = null;
      if (ProjectStamps.PORTABLE_CACHES && myCacheDownloadSettings != null) {
        LOG.info("Cache download settings: disableDownload=" + myCacheDownloadSettings.getDisableDownload() +
                 "; forceUpdate=" + myCacheDownloadSettings.getForceDownload() +
                 "; cleanupAsynchronously=" + myCacheDownloadSettings.getCleanupAsynchronously());
        if (myCacheDownloadSettings.getDisableDownload()) {
          LOG.info("Cache download is disabled");
        } else {
          LOG.info("Trying to download JPS caches before build");
          myCacheLoadManager = new JpsOutputLoaderManager(myBuildRunner.loadModelAndGetJpsProject(), this, myProjectPath, myChannel,
                                                          mySessionId, myCacheDownloadSettings);
          myCacheLoadManager.load(myBuildRunner, true, myScopes, () -> {
            if (myPreloadedData != null) {
              LOG.info("Releasing old project description...");
              ProjectDescriptor projectDescriptor = myPreloadedData.getProjectDescriptor();
              if (projectDescriptor != null) {
                projectDescriptor.release();
                myPreloadedData.setProjectDescriptor(null);
              }
              JpsServiceManager.getInstance().getExtensions(PreloadedDataExtension.class).forEach(ext -> ext.discardPreloadedData(myPreloadedData));
              myPreloadedData = null;
            }
          });
        }
      }

      runBuild(new MessageHandler() {
        @Override
        public void processMessage(BuildMessage buildMessage) {
          final CmdlineRemoteProto.Message.BuilderMessage response;
          if (buildMessage instanceof FileGeneratedEvent) {
            final Collection<Pair<String, String>> paths = ((FileGeneratedEvent)buildMessage).getPaths();
            response = !paths.isEmpty() ? CmdlineProtoUtil.createFileGeneratedEvent(paths) : null;
          }
          else if (buildMessage instanceof DoneSomethingNotification) {
            doneSomething.set(true);
            response = null;
          }
          else if (buildMessage instanceof CompilerMessage) {
            doneSomething.set(true);
            final CompilerMessage compilerMessage = (CompilerMessage)buildMessage;
            final String compilerName = compilerMessage.getCompilerName();
            final String text = !StringUtil.isEmptyOrSpaces(compilerName)? compilerName + ": " + compilerMessage.getMessageText() : compilerMessage.getMessageText();
            final BuildMessage.Kind kind = compilerMessage.getKind();
            if (kind == BuildMessage.Kind.ERROR) {
              hasErrors.set(true);
            }
            response = CmdlineProtoUtil.createCompileMessage(
              kind, text, compilerMessage.getSourcePath(),
              compilerMessage.getProblemBeginOffset(), compilerMessage.getProblemEndOffset(),
              compilerMessage.getProblemLocationOffset(), compilerMessage.getLine(), compilerMessage.getColumn(),
              -1.0f, compilerMessage.getModuleNames());
          }
          else if (buildMessage instanceof CustomBuilderMessage) {
            CustomBuilderMessage builderMessage = (CustomBuilderMessage)buildMessage;
            response = CmdlineProtoUtil.createCustomBuilderMessage(builderMessage.getBuilderId(), builderMessage.getMessageType(), builderMessage.getMessageText());
          }
          else if (buildMessage instanceof BuilderStatisticsMessage) {
            final BuilderStatisticsMessage message = (BuilderStatisticsMessage)buildMessage;
            final boolean worthReporting = message.getNumberOfProcessedSources() != 0 || message.getElapsedTimeMs() > 50;
            if (worthReporting) {
              LOG.info(message.getMessageText());
            }
            //noinspection HardCodedStringLiteral
            response = worthReporting && REPORT_BUILD_STATISTICS ?
              CmdlineProtoUtil.createCompileMessage(BuildMessage.Kind.JPS_INFO, message.getMessageText(), null, -1, -1, -1, -1, -1, -1.0f, Collections.emptyList()) : null;
          }
          else if (!(buildMessage instanceof BuildingTargetProgressMessage)) {
            float done = -1.0f;
            if (buildMessage instanceof ProgressMessage) {
              done = ((ProgressMessage)buildMessage).getDone();
            }
            //noinspection HardCodedStringLiteral
            response = CmdlineProtoUtil.createCompileProgressMessageResponse(buildMessage.getMessageText(), done);
          }
          else {
            response = null;
          }
          if (response != null) {
            myChannel.writeAndFlush(CmdlineProtoUtil.toMessage(mySessionId, response));
          }
        }
      }, this);

      if (profilingHelper != null) {
        profilingHelper.stopProfiling();
      }
    }
    catch (Throwable e) {
      LOG.info(e);
      error = e;
    }
    finally {
      logStorageDiagnostic();
      finishBuild(error, hasErrors.get(), doneSomething.get());
      memWatcher.shutdown();
    }
  }
@Override
  public void run()