private void collectAndRegisterOtherDebuggersThreads(List<PyThreadInfo> threads) {
   for (RemoteDebugger d : getOtherDebuggers()) {
     threads.addAll(d.getThreads());
     for (PyThreadInfo t : d.getThreads()) {
       myThreadRegistry.register(t.getId(), d);
     }
   }
 }
  @Override
  public void waitForConnect() throws Exception {
    try {
      //noinspection SocketOpenedButNotSafelyClosed
      final Socket socket = myServerSocket.accept();

      ApplicationManager.getApplication()
          .executeOnPooledThread(
              new Runnable() {
                @Override
                public void run() {
                  try {
                    // do we need any synchronization here with myMainDebugger.waitForConnect() ???
                    // TODO
                    sendDebuggerPort(socket, myDebugServerSocket, myDebugProcess);
                  } catch (Exception e) {
                    throw new RuntimeException(e);
                  }
                }
              });

      myMainDebugger.waitForConnect();

      disposeAcceptor();

      myDebugProcessAcceptor = new DebuggerProcessAcceptor(this, myServerSocket);
      ApplicationManager.getApplication().executeOnPooledThread(myDebugProcessAcceptor);
    } finally {

    }
  }
  private List<PyThreadInfo> collectAllThreads() {
    List<PyThreadInfo> result = Lists.newArrayList();

    result.addAll(myMainDebugger.getThreads());

    // collect threads and add them to registry to faster access
    // we don't register mainDebugger as it is default if there is no mapping
    for (RemoteDebugger d : myOtherDebuggers) {
      result.addAll(d.getThreads());
      for (PyThreadInfo t : d.getThreads()) {
        myThreadRegistry.register(t.getId(), d);
      }
    }

    return result;
  }
  private void removeDisconnected(ArrayList<RemoteDebugger> debuggers) {
    boolean allConnected = true;
    for (RemoteDebugger d : debuggers) {
      if (!d.isConnected()) {
        allConnected = false;
      }
    }
    if (!allConnected) {
      List<RemoteDebugger> newList = Lists.newArrayList();
      for (RemoteDebugger d : debuggers) {
        if (d.isConnected()) {
          newList.add(d);
        }
      }

      synchronized (myOtherDebuggers) {
        myOtherDebuggers.clear();
        myOtherDebuggers.addAll(newList);
      }
    }
  }
    @Override
    public void run() {
      while (myShouldAccept) {
        try {
          final ServerSocket serverSocketCopy = myServerSocket;
          if (serverSocketCopy == null) {
            break;
          }
          Socket socket = serverSocketCopy.accept();

          try {
            final ServerSocket serverSocket = createServerSocket();
            final RemoteDebugger debugger =
                new RemoteDebugger(
                    myMultiProcessDebugger.myDebugProcess,
                    serverSocket,
                    myMultiProcessDebugger.myTimeoutInMillis);
            addCloseListener(debugger);
            sendDebuggerPort(socket, serverSocket, myMultiProcessDebugger.myDebugProcess);
            socket.close();
            debugger.waitForConnect();
            debugger.handshake();
            myMultiProcessDebugger.addDebugger(debugger);
            myMultiProcessDebugger.myDebugProcess.init();

            debugger.run();
          } catch (Exception e) {
            LOG.warn(e);
          } finally {
            if (!socket.isClosed()) {
              socket.close();
            }
          }
        } catch (Exception ignore) {
          if (myServerSocket == null) {
            myShouldAccept = false;
          }
        }
      }
    }
  @Override
  public Collection<PyThreadInfo> getThreads() {
    List<PyThreadInfo> threads = Lists.newArrayList(myMainDebugger.getThreads());

    List<PyThreadInfo> result = Lists.newArrayList();

    cleanOtherDebuggers();

    collectAndRegisterOtherDebuggersThreads(
        threads); // we don't register mainDebugger as it is default if there is no mapping

    if (myOtherDebuggers.size() > 0) {
      // here we add process id to thread name in case there are more then one process
      threads = addProcessIdToThreadName(threads, result);
    }

    return Collections.unmodifiableCollection(threads);
  }
    private void addCloseListener(@NotNull final RemoteDebugger debugger) {
      debugger.addCloseListener(
          new RemoteDebuggerCloseListener() {
            @Override
            public void closed() {
              notifyThreadsClosed(debugger);
            }

            @Override
            public void communicationError() {
              notifyThreadsClosed(debugger);
            }

            @Override
            public void detached() {
              notifyThreadsClosed(debugger);
            }
          });
    }
 public void removeCloseListener(RemoteDebuggerCloseListener listener) {
   myMainDebugger.removeCloseListener(listener);
 }
 @Override
 public boolean isConnected() {
   return myMainDebugger.isConnected();
 }
 @Override
 public void removeExceptionBreakpoint(ExceptionBreakpointCommandFactory factory) {
   for (RemoteDebugger d : allDebuggers()) {
     d.execute(factory.createRemoveCommand(d));
   }
 }
 public void addCloseListener(RemoteDebuggerCloseListener listener) {
   myMainDebugger.addCloseListener(listener);
 }
 @Override
 public void run() throws PyDebuggerException {
   myMainDebugger.run();
 }
 @Override
 public String loadSource(String path) {
   return myMainDebugger.loadSource(path);
 }
 @Override
 public String handshake() throws PyDebuggerException {
   return myMainDebugger.handshake();
 }