/**
   * block thread until event available
   *
   * @return available event or null if no event available and return-threshold is reach
   * @throws InterruptedException
   */
  public EventType waitNewEvent(int waitTimeout) throws InterruptedException {

    this.returnCount = 0;

    while (true) {

      synchronized (this.queue) {
        if (Options.LL_DEBUG) {

          BtLog.d("EventQueue.waitNewEvent() before remove queue size:" + this.queue.size());
        }

        // event available => return first event
        if (!this.queue.isEmpty()) {

          return this.queue.removeFirst();
        }
        // return threshold is enabled when it's > 0
        else if (this.returnThreshold > 0) {

          // event not available => check return threshold
          // this is for the abnormal case and avoid infinite loop situation (no end event)
          this.returnCount++;
          if (this.returnCount >= this.returnThreshold) {

            return null;
          }
        }

        // wait until event available or timeout
        try {
          if (this.isCanceled) {

            this.isCanceled = false;
            return null;
          }
          this.queue.wait(waitTimeout);
          if (this.isCanceled) {

            this.isCanceled = false;
            return null;
          }
        } catch (InterruptedException ie) {

          BtLog.i(
              "EventQueue.waitNewEvent() thread["
                  + Thread.currentThread().getName()
                  + "] interrupted");
          throw ie;
        } catch (Exception ex) {

          BtLog.e("EventQueue.waitNewEvent() error: " + ex.getMessage());
          throw new IllegalStateException("EventQueue.waitNewEvent() error.", ex);
        }
      }
    }
  }
  /**
   * add event into this queue and notify waiting thread to process
   *
   * @param newEvent
   */
  public void notifyNewEvent(EventType newEvent) {

    synchronized (this.queue) {
      this.queue.addLast(newEvent);

      if (Options.LL_DEBUG) {

        BtLog.d("EventQueue.notifyNewEvent() after insert queue size:" + this.queue.size());
      }
      this.queue.notify();
    }
  }
  /** return null from waitNewEvent() */
  public void cancelWaitNewEvent() {

    synchronized (this.queue) {
      this.isCanceled = true;

      if (Options.LL_DEBUG) {

        BtLog.d("EventQueue.cancelWaitNewEvent():" + this.queue.size());
      }
      this.queue.notify();
    }
  }