@NotNull
  public static <T, O extends Watchable> T create(@NotNull final O target, final ProcessHandler process) {
    final Pair<LinkedBlockingQueue<Call>, LinkedBlockingQueue<Call.Result>> queue =
      Pair.create(new LinkedBlockingQueue<>(10), new LinkedBlockingQueue<>());

    final Thread thread = new Thread("Async Invocation Thread for " + process) {
      @Override
      public void run() {
        try {
          while (!Thread.currentThread().isInterrupted()) {
            final Call call = queue.first.take();
            queue.second.offer(call.invoke());
          }
        } catch (InterruptedException e) {
          // break
        }
      }
    };
    thread.start();

    final AtomicBoolean ref = new AtomicBoolean();
    final Disposable d = new Disposable() {
      boolean disposed;

      @Override
      public void dispose() {
        if (!disposed) {
          disposed = true;

          ref.set(true);
          thread.interrupt();
        }
      }
    };
    process.addProcessListener(new ProcessAdapter() {
      @Override
      public void processTerminated(@NotNull ProcessEvent event) {
        synchronized (d) {
          Disposer.dispose(d);
        }
      }

      @Override
      public void processWillTerminate(@NotNull ProcessEvent event, boolean willBeDestroyed) {
        if (!willBeDestroyed) {
          synchronized (d) {
            Disposer.dispose(d);
          }
        }
      }
    });

    final Alarm alarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, d);
    final Alarm alarm2 = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, alarm);

    final Runnable watchdog = () -> ref.set(true);

    final Runnable ping = new Runnable() {
      @Override
      public void run() {
        synchronized (d) {
          if (alarm.isDisposed()) {
            return;
          }

          alarm2.addRequest(watchdog, 200);
          try {
            ref.set(!target.ping());
          } catch (Exception e) {
            ref.set(true);
          } finally {
            alarm2.cancelRequest(watchdog);
            alarm.addRequest(this, 500);
          }
        }
      }
    };
    alarm.addRequest(ping, 500);

    final EDTGuard guard = new EDTGuard(target, queue, ref);
    final ClassLoader classLoader = target.getClass().getClassLoader();
    final Class<?>[] interfaces = target.getClass().getInterfaces();
    //noinspection unchecked
    return (T)Proxy.newProxyInstance(classLoader, interfaces, guard);
  }
@NotNull
  public static <T, O extends Watchable> T create(@NotNull final O target, final ProcessHandler process)