Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8073442

add Objects.await convenience method for testing a condition

    XMLWordPrintable

Details

    • Enhancement
    • Status: Open
    • P5
    • Resolution: Unresolved
    • None
    • None
    • core-libs

    Description

      The use of synchronization and Object.wait() is notoriously error-prone. It must test a condition in a loop, and it must catch InterruptedException. For this reason, most code gets this wrong. It would be nice to have a convenience method that provides correct and typical handling for these cases. The desired semantics might be:

       - take the lock on an object
       - while the predicate is not true, call wait()
       - when the predicate is true, return true
       - if wait() is interrupted, return false

      This could be added to java.util.Objects, and the implementation might look something like the following:

      public static <T> boolean await(T obj, Predicate<? super T> pred) {
          Objects.requireNonNull(obj);
          Objects.requireNonNull(pred);
          try {
              synchronized (obj) {
                  while (! pred.test(obj)) {
                      obj.wait();
                  }
                  return true;
              }
          } catch (InterruptedException ie) {
              Thread.currentThread().interrupt();
              return false;
          }
      }

      For example, consider code like this that locks/waits/notifies a non-thread-safe data structure "queue".

              synchronized (queue) {
                  while (queue.size() == 0) {
                      try {
                          queue.wait();
                      } catch (InterruptedException e) {
                      }
                  }
                  item = queue.remove(0);
              }
              process(item);

      (This is adapted from the RMI TCPChannel.java code.) Note the mishandling of InterruptedException. This could be rewritten as follows:

              if (Objects.await(queue, q -> q.size() > 0)) {
                  process(queue.remove(0));
              } else {
                  // interrupted: return, break out of loop, etc.
              }

      BUT NOTE: this is incorrect. The removal from the queue is performed outside the lock! Clearly further design work is necessary; perhaps another lambda -- a Consumer<T> -- could be passed as well, and this would be performed while the lock is held after the predicate is true.

      A common use case would be to assign something to a local variable, and this isn't possible from within a lambda. Perhaps the return value can be used for this, instead.

      Variations might include timeout handling and throwing of InterruptedException.

      Attachments

        Activity

          People

            Unassigned Unassigned
            smarks Stuart Marks
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated: