public void registerTimeoutHandler(int commandId, int timeout, Runnable onTimeout) {
   synchronized (myLock) {
     myTimeoutHandlers.put(
         commandId, new TimeoutHandler(onTimeout, System.currentTimeMillis() + timeout));
   }
   scheduleTimeoutCheck();
 }
 private void scheduleTimeoutCheck() {
   final Ref<Long> nextTime = Ref.create(Long.MAX_VALUE);
   synchronized (myLock) {
     if (myTimeoutHandlers.isEmpty()) return;
     myTimeoutHandlers.forEachValue(
         new TObjectProcedure<TimeoutHandler>() {
           public boolean execute(TimeoutHandler handler) {
             nextTime.set(Math.min(nextTime.get(), handler.myLastTime));
             return true;
           }
         });
   }
   final int delay = (int) (nextTime.get() - System.currentTimeMillis() + 100);
   LOG.debug("schedule timeout check in " + delay + "ms");
   if (delay > 10) {
     myTimeoutAlarm.cancelAllRequests();
     myTimeoutAlarm.addRequest(() -> checkTimeout(), delay);
   } else {
     checkTimeout();
   }
 }
 public void checkTimeout() {
   LOG.debug("Checking timeout");
   final List<TimeoutHandler> timedOut = new ArrayList<>();
   synchronized (myLock) {
     final long time = System.currentTimeMillis();
     myTimeoutHandlers.retainEntries(
         new TIntObjectProcedure<TimeoutHandler>() {
           public boolean execute(int a, TimeoutHandler b) {
             if (time > b.myLastTime) {
               timedOut.add(b);
               return false;
             }
             return true;
           }
         });
   }
   for (TimeoutHandler handler : timedOut) {
     LOG.debug("performing timeout action: " + handler.myAction);
     handler.myAction.run();
   }
   scheduleTimeoutCheck();
 }