On 21/08/2023 11:56 pm, Albert Attard wrote:
Hello.

Thank you very much Pavel for helping me find more information about this.

After some research (mainly by Pavel) we found a thread, with subject "/Default Functions for Lock Interface/", from October 2013 that discussed this exact thing.  I don't believe that this thread is available on the internet, but I may be wrong.

In this thread, two approaches were discussed.

 1. Add support for the try-with-resources
 2. Add default methods to the Lock interface


The second approach makes use of lambda functions which incur performance cost when data is modified within the lambda function, as described by Brian Goetz in the same thread (on the 8th of October 2013).

---
/When you write a method like

void withLock(Runnable)

the Runnable is going to have side-effects. So its going to be a capturing lambda -- one that captures variables from its scope:

withLock( () -> { counter++; } ); // counter is captured

With the current implementation of lambda, evaluating (not invoking) a capturing lambda expression will cause an allocation. Whereas the hand-unrolled version:

lock.lock();
try { counter++; }
finally { lock.unlock(); }

does not. The sort of things people do with locks are generally pretty performance-sensitive, so such an API was deemed, at the current time, to be an "attractive nuisance."
/---

Almost 10 years have passed since this was originally suggested.  Have enough things changed since then, that would make this approach feasible?

Long before lambda's Doug Lea originally had this in the form of LockedExecutor:

https://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/LockedExecutor.html

but it never made the cut into java.util.concurrent (JSR-166).

A "withLock" style of programming does have some appeal to some people but it was never considered worthy of a place in the core API. The basic thing is trivial but then there are issues about returning values, or specialized exception handling - all of which are best handled, and easily handled, by application logic.

Cheers,
David
-----

With kind regards,
Albert Attard


On Mon, 21 Aug 2023 at 15:33, John Hendrikx <john.hendr...@gmail.com <mailto:john.hendr...@gmail.com>> wrote:

    I couldn't find a discussion on openjdk, but for those interested (and
    to save others some searching) there is a JBS ticket:

    https://bugs.openjdk.org/browse/JDK-8025597
    <https://bugs.openjdk.org/browse/JDK-8025597>

    --John

    On 21/08/2023 14:37, Pavel Rappo wrote:
     > This is suggested every once in a while. I appreciate that
    openjdk mailing lists are not easily searchable, but with a bit of
    skill, you could find a few previous discussions on the topic.
     >
     > This has also been discussed on concurrency-interest (at
    cs.oswego.edu <http://cs.oswego.edu> <http://cs.oswego.edu/
    <http://cs.oswego.edu/>>), a dedicated mailing list for concurrency
    in Java. Sadly, that list has been defunct for quite some time now.
     >
     > -Pavel
     >
     >> On 21 Aug 2023, at 13:18, Albert Attard <albertatt...@gmail.com
    <mailto:albertatt...@gmail.com>> wrote:
     >>
     >> Hello.
     >>
     >> I hope all is well.
     >>
     >> Do you believe it is a bad idea to enrich the Lock interface
    with a set of default methods that safely release the lock once ready?
     >>
     >> Consider the following (dangerous) example.
     >>
     >> final Lock lock = new ReentrantLock ();
     >> lock.lock();
     >> /* Code that may throw an exception */
     >> lock.unlock();
     >>
     >> This example will never release the lock if an exception is
    thrown, as the programmer didn’t wrap this up in a try/finally.
     >>
     >> Adding a default method within the Lock interface, called
    withLock(Runnable) for example or any better name, would streamline
    this, as shown next.
     >>
     >> default void withLock(final Runnable runnable) {
     >>      requireNonNull(runnable, "Cannot run a null");
     >>      lock();
     >>      try {
     >>          runnable.run();
     >>      } finally {
     >>          unlock();
     >>      }
     >> }
     >>
     >> The caller can now simply change the above example into the
    following, without having to worry about this.
     >>
     >> final Lock lock = new ReentrantLock ();
     >> lock.withLock(() -> {
     >>    /* Code that may throw an exception */
     >> });
     >>
     >> We can have more variants of these default methods, as shown next.
     >>
     >> default <T> T getWithLock(final Supplier<T> supplier) {
     >>      requireNonNull(supplier, "The supplier cannot be null");
     >>      lock();
     >>      try {
     >>          return supplier.get();
     >>      } finally {
     >>          unlock();
     >>      }
     >> }
     >>
     >> Any thoughts?
     >>
     >> With kind regards,
     >> Albert Attard

Reply via email to