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 <[email protected]
<mailto:[email protected]>> 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 <[email protected]
<mailto:[email protected]>> 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