/**
  * Wait until the semaphore is full or the timer expires. If the semaphore is already full, return
  * immediately. Use {@link Deadline#expire()} to make this return early. Does not change the state
  * of the semaphore.
  *
  * @param timer time to wait. If null, returns immediately.
  * @return true if <code>take()</code> was successful (semaphore was or became full), else false
  *     (timer expired).
  * @throws InterruptedException if interrupted while waiting
  */
 public synchronized boolean waitFull(Deadline timer) throws InterruptedException {
   if (timer != null) {
     Deadline.InterruptCallback cb = new Deadline.InterruptCallback();
     try {
       timer.registerCallback(cb);
       while (!state && !timer.expired()) {
         this.wait(timer.getSleepTime());
       }
     } finally {
       cb.disable();
       timer.unregisterCallback(cb);
     }
   }
   return state;
 }