Language extensions for java.util.concurrent?

classic Classic list List threaded Threaded
18 messages Options
Reply | Threaded
Open this post in threaded view
|

Language extensions for java.util.concurrent?

Dawid Kurzyniec
Now, as the dust has settled and java.util.concurrent is a core library,
I am wondering if the next step should be to propose language extensions
to simplify usage of new tools, particularly locks and atomics? For
instance:

class A {
  atomic int a;
  final Lock lock = new ReentrantLock();
  void foo() {
    ++a; // -> incrementAndGet()
    a ?= 5, 8; // ->compareAndSet(5, 8)
    lock.lock() { // ->lock(); try { ... } finally { unlock(); }
      // do stuff with the lock held
    }
  }
}

Precedence for interface-based syntax support has been set by the
Iterable, and atomic is essentially a stronger incarnation of volatile.
So why not?

Regards,
Dawid

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions for java.util.concurrent?

Jeremy Manson
Dawid Kurzyniec wrote:

> Now, as the dust has settled and java.util.concurrent is a core library,
> I am wondering if the next step should be to propose language extensions
> to simplify usage of new tools, particularly locks and atomics? For
> instance:
>
> class A {
>  atomic int a;
>  final Lock lock = new ReentrantLock();
>  void foo() {
>    ++a; // -> incrementAndGet()
>    a ?= 5, 8; // ->compareAndSet(5, 8)
>    lock.lock() { // ->lock(); try { ... } finally { unlock(); }
>      // do stuff with the lock held
>    }
>  }
> }
>
> Precedence for interface-based syntax support has been set by the
> Iterable, and atomic is essentially a stronger incarnation of volatile.
> So why not?
>

Three things:

First, I don't think you would be able to convince the powers that be to
introduce new syntax to support an API.  It tends to be something they
don't like.  Generally, their feeling seems to be that APIs are the
correct way to extend the language.  Which is why we get APIs that
require rewrites of the entire system, like RTSJ and Isolates.

Of course, you can argue about the basic correctness of this view.  And
you can argue about the presence of autoboxing and foreach loops.

Second, I, personally, would be more inclined to want to see a general
purpose atomic block.  Something like this:

class A {
  int a;
  final Lock lock = new ReentrantLock();
  void foo() {
    atomic {
      ++a;
    }
    atomic {
      if (a == 5) {
        a = 8;
      }
    }
  }
}


Compilers could do the appropriate replacement, changing the first block
to an increment and the second to something like a CAS.  The syntax
could also be used to make multiple statements atomic, rather than being
restricted to a single statement.  There is some momentum for such a
creature in the academic community.

Third, having said this, it is possible to do some method-level stuff
with annotations.  For example, I wrote a program that takes classes
that look like this:

class Foo {

   @reentrant void a() {
      // does stuff
   }

}

and changes them to:

class Foo {

   ReentrantLock rl = new ReentrantLock();

   void a() {
     rl.lock();
     try {
       // does stuff
     } finally {
       rl.unlock();
     }
   }
}

which may suffice for new syntax for locking.  It just took a little bit
of bytecode munging.

                                        Jeremy
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions for java.util.concurrent?

Dawid Kurzyniec
Jeremy Manson wrote:

> Dawid Kurzyniec wrote:
>
>> Now, as the dust has settled and java.util.concurrent is a core
>> library, I am wondering if the next step should be to propose
>> language extensions to simplify usage of new tools, particularly
>> locks and atomics? For instance:
>>
>> class A {
>>  atomic int a;
>>  final Lock lock = new ReentrantLock();
>>  void foo() {
>>    ++a; // -> incrementAndGet()
>>    a ?= 5, 8; // ->compareAndSet(5, 8)
>>    lock.lock() { // ->lock(); try { ... } finally { unlock(); }
>>      // do stuff with the lock held
>>    }
>>  }
>> }
>>
>> Precedence for interface-based syntax support has been set by the
>> Iterable, and atomic is essentially a stronger incarnation of
>> volatile. So why not?
>>
>
> Three things:
>
> First, I don't think you would be able to convince the powers that be
> to introduce new syntax to support an API.  It tends to be something
> they don't like.  Generally, their feeling seems to be that APIs are
> the correct way to extend the language.  Which is why we get APIs that
> require rewrites of the entire system, like RTSJ and Isolates.
>
> Of course, you can argue about the basic correctness of this view.  
> And you can argue about the presence of autoboxing and foreach loops.
>
Sure, they are, and they ought to be, very conservative about the
language. But nonwithstanding, we see the language evolving, with
changes nearly every major release. Inner classes, anonymous array
expressions, class literals, blank finals, final parameters in 1.1,
strictfp in 1.2, assert in 1.4, generics, autoboxing, foreach loops,
varargs, concise array literals in 5.0.

Personally I don't have a problem with using j.u.c. merely as the API,
but the concurrency is already a part of the language anyway, so I think
that a bit of syntactic sugar for locks and atomics could decrease the
dichotomy between intrinsic locks and volatiles on one side and explicit
locks and atomics on the other. After all, this is a very special API,
requiring explicit JVM support (atomics and park/unpark), and receiving
special treatment already (deadlock detection for explicit locks). So,
if there is any API for which I would welcome language support, it would
be this one.

> Second, I, personally, would be more inclined to want to see a general
> purpose atomic block.  Something like this:
>
> class A {
>  int a;
>  final Lock lock = new ReentrantLock();
>  void foo() {
>    atomic {
>      ++a;
>    }
>    atomic {
>      if (a == 5) {
>        a = 8;
>      }
>    }
>  }
> }
>
>
> Compilers could do the appropriate replacement, changing the first
> block to an increment and the second to something like a CAS.  The
> syntax could also be used to make multiple statements atomic, rather
> than being restricted to a single statement.  There is some momentum
> for such a creature in the academic community.

I am not sure how multiple statements could be make atomic without
introducing mutual exclusion. But if you go there, the boundary between
blocking and non-blocking statements becomes blurry... I feel a bit
uneasy about that.

Also, the syntax for CAS is actually longer in this form than
a.compareAndSet().

Anyway, I am not saying I have all the answers. I am just curious what
other people may think in general about introducing language extensions
like this to support this API.

In particular, if I had lock.lock() { ... } (and lock.tryLock() { ... }
etc.), I would be more inclined to move away from intrinsic locks
towards explicit locks.

Perhaps the idea could be generalized to allow general constructs of the
form foo.bar() { ... }, with before- and after- actions for bar()
defined by the class via annotations, and inserted by the source
compiler as needed. This would also in some sense generalize the
constructor invocation syntax for anonymous inner classes. Such a
general mechanism could benefit other APIs that rely on before/after
pattern. For instance, I can imagine a version of doPrivileged like this:

AccessController.doPrivileged(acc) {
   // statements here;
   // can read/write local variables from enclosing block
}

The remaining questions are what to do with a returned value (if the
method is non-null), and how to handle exceptions. One possible solution:

try {
  Socket s = pool.getSocket() {
    // after-action returns the socket to the pool, but only if there
was no IOException
  }
}
catch (IOException e) {
  // socket could not be obtained, or broken connection
  // handle...
}

Regards,
Dawid

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions for java.util.concurrent?

Jeremy Manson
Dawid Kurzyniec wrote:
>
> I am not sure how multiple statements could be make atomic without
> introducing mutual exclusion. But if you go there, the boundary
> between blocking and non-blocking statements becomes blurry... I feel
> a bit uneasy about that.

There are a lot of techniques in the works to do just this - it is a
common research problem.  The idea is to import optimistic concurrency
from databases.  The back end would take care of atomicity and
visibility constraints, so the programmer could be free not to worry
about that stuff.

I'm not sure why this would make you uneasy, though.  Databases employ
this sort of concurrency all the time.  Wouldn't it be nice if everyone
could write non-blocking data structures, instead of just people with
Ph.D.s?  Also, if you can use atomic blocks instead of locking, many of
the concerns of locking (deadlock, for example) become simplified
substantially.

> Also, the syntax for CAS is actually longer in this form than a.compareAndSet().

Yes, but it has the virtue of being comprehensible for those who don't
know what a compareAndSet is, without extra work to go look it up.

I'm not claiming that such a structure would be a panacea, but I think
it would be a lot nicer than special syntax for each method that could
be invoked on an AtomicXXX.  I am also concerned that your syntax would
discourage programmers from thinking about multi-variable invariants -
i.e., it would make it TOO simple to have a single atomic action that
doesn't relate to other associated actions correctly.  OTOH, an atomic
{...} block might encourage programmers to group related actions that
should be performed atomically.

> Perhaps the idea could be generalized to allow general constructs of
> the form foo.bar() { ... }, with before- and after- actions for bar()
>  defined by the class via annotations, and inserted by the source
> compiler as needed. This would also in some sense generalize the
> constructor invocation syntax for anonymous inner classes. Such a
> general mechanism could benefit other APIs that rely on before/after
>  pattern. For instance, I can imagine a version of doPrivileged like
> this:
>
> AccessController.doPrivileged(acc) { // statements here; // can
> read/write local variables from enclosing block }
>

This is fairly close to what my annotation tool actually does.  More
specifically, you can define something that looks like this in a special
class:

     public ReentrantLock rl = new ReentrantLock();

     void around() {
        rl.lock();
        try {
          proceed();
        } finally {
          rl.unlock();
        }
     }

And you can define the following method:

@reentrant int f() {
   a.x = 1;
   return a.x;
}

And it will transform it (at the bytecode level) into this:

int f() {
   int returnVal;
   rl.lock();
   try {
     returnVal = realF();
   } finally {
     rl.unlock();
   }
   return returnVal;
}

int realF() {
   a.x = 1;
   return a.x;
}

You can adjust the around() method as desired to do pretty much whatever
you want it to do.  The problem is that you can't have it within a
method scope (because you can't annotate individual statements).

                                        Jeremy
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions forjava.util.concurrent?

Dawid Kurzyniec
Jeremy Manson wrote:

> Dawid Kurzyniec wrote:
>
>>
>> I am not sure how multiple statements could be make atomic without
>> introducing mutual exclusion. But if you go there, the boundary
>> between blocking and non-blocking statements becomes blurry... I feel
>> a bit uneasy about that.
>
>
> There are a lot of techniques in the works to do just this - it is a
> common research problem.  The idea is to import optimistic concurrency
> from databases.  The back end would take care of atomicity and
> visibility constraints, so the programmer could be free not to worry
> about that stuff.
>
> I'm not sure why this would make you uneasy, though.  Databases employ
> this sort of concurrency all the time.  Wouldn't it be nice if
> everyone could write non-blocking data structures, instead of just
> people with Ph.D.s?  Also, if you can use atomic blocks instead of
> locking, many of the concerns of locking (deadlock, for example)
> become simplified substantially.
>
The "uneasiness" was because I thought that locks would be silently
inserted to handle more complex atomic blocks. If atomic {} is always
non-blocking, then I may buy into the idea. But optimistic concurrency
algorithms must deal with failures and retries. How would that look
like? And what does the following mean:

atomic {
   if (a == 6) { foo(); b=4; a=5; } else a = 4;
}

Would the compiler have to ensure that a is not changed by other threads
after it is read in the atomic block? But how to do that without
blocking that other threads?

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions forjava.util.concurrent?

Jeremy Manson
Dawid Kurzyniec wrote:

> The "uneasiness" was because I thought that locks would be silently
> inserted to handle more complex atomic blocks. If atomic {} is always
> non-blocking, then I may buy into the idea. But optimistic concurrency
> algorithms must deal with failures and retries. How would that look
> like? And what does the following mean:
>
> atomic {
>   if (a == 6) {
 >     foo();
 >     b=4;
 >     a=5;
 >   } else a = 4;
> }
>
> Would the compiler have to ensure that a is not changed by other threads
> after it is read in the atomic block? But how to do that without
> blocking that other threads?

There are several ways of ensuring this, most of which just take some
imagination; the "right" one probably depends on what you need from your
system.  Here's some brief discussion, which is by no means complete.

First, you might say that "atomic" only means atomic if a given variable
is always accessed in an atomic section.  Otherwise, the multivariable
invariants don't hold.  So, for example, a thread which accesses b and a
outside an atomic block might see b == 4, but a == something else.  This
may seem bad, but it is roughly analogous to writing code with data
races in it.

That would ensure that atomic blocks only need to be atomic with respect
to each other.  And this is a significantly easier problem.

One simple solution: record all writes in an atomic block to a log,
instead of writing them directly to memory.  At the end of the block, if
the memory that has been accessed has not changed, then briefly block
(in the same sense that all machine level atomic actions briefly block)
and perform all updates atomically.  If the memory has been changed,
then restart.

There are obviously pitfalls and elided details here.  There are also
more sophisticated solutions out there - like I said, it is a fairly
active research area.  If you are interested in doing some reading, a
good place to start might be Harris and Fraser's 2003 OOPSLA paper,
"Language Support for Lightweight Transactions".

                                        Jeremy
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensionsforjava.util.concurrent?

Dawid Kurzyniec
Jeremy Manson wrote:

> Dawid Kurzyniec wrote:
>
>> The "uneasiness" was because I thought that locks would be silently
>> inserted to handle more complex atomic blocks. If atomic {} is always
>> non-blocking, then I may buy into the idea. But optimistic
>> concurrency algorithms must deal with failures and retries. How would
>> that look like? And what does the following mean:
>>
>> atomic {
>>   if (a == 6) {
>
> >     foo();
> >     b=4;
> >     a=5;
> >   } else a = 4;
>
>> }
>>
>> Would the compiler have to ensure that a is not changed by other
>> threads after it is read in the atomic block? But how to do that
>> without blocking that other threads?
>
>
> There are several ways of ensuring this, most of which just take some
> imagination; the "right" one probably depends on what you need from
> your system.  Here's some brief discussion, which is by no means
> complete.
>
> First, you might say that "atomic" only means atomic if a given
> variable is always accessed in an atomic section.  Otherwise, the
> multivariable invariants don't hold.  So, for example, a thread which
> accesses b and a outside an atomic block might see b == 4, but a ==
> something else.  This may seem bad, but it is roughly analogous to
> writing code with data races in it.
>
> That would ensure that atomic blocks only need to be atomic with
> respect to each other.  And this is a significantly easier problem.
>
> One simple solution: record all writes in an atomic block to a log,
> instead of writing them directly to memory.  At the end of the block,
> if the memory that has been accessed has not changed, then briefly
> block (in the same sense that all machine level atomic actions briefly
> block) and perform all updates atomically.  If the memory has been
> changed, then restart.


Jeremy, thanks for the info and references I find it very interesting.

Restarting would require that all invoked methods are idempotent, but I
think that invoking a method from an atomic block borders with abuse anyway.

Abuse of atomic blocks would cause livelocks, just like abuse of
synchronized blocks causes deadlocks. Interesting.

Regards,
Dawid

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions for java.util.concurrent?

Elias Ross-5
In reply to this post by Jeremy Manson
On Thu, 2005-11-10 at 14:15 -0500, Jeremy Manson wrote:

>      public ReentrantLock rl = new ReentrantLock();
>
>      void around() {
>         rl.lock();
>         try {
>           proceed();
>         } finally {
>           rl.unlock();
>         }
>      }

Too bad Java doesn't have the C# keyword "using", it would be nice to be
able to write

     void around() {
        using (rt) {
          proceed();
        }
     }

It's too easy to accidentally write:

     void around() {
        try {
          rl.lock();
          proceed();
        } finally {
          rl.unlock();
        }
     }


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions forjava.util.concurrent?

Dawid Kurzyniec
Elias Ross wrote:

>Too bad Java doesn't have the C# keyword "using", it would be nice to be
>able to write
>
>     void around() {
>        using (rt) {
>          proceed();
>        }
>     }
>
>It's too easy to accidentally write:
>
>     void around() {
>        try {
>          rl.lock();
>          proceed();
>        } finally {
>          rl.unlock();
>        }
>     }
>
>  
>
 From this example, "using" seems to me to be just like "synchronized".  
(I don't know C#). The problem with this syntax is that it does not
allow using tryLock(...), and lockInterruptibly(). That's why I would
opt for lock.lock() { ... }

Regards,
Dawid

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions for java.util.concurrent?

Gregg Wonderly-2
In reply to this post by Elias Ross-5


Elias Ross wrote:
> Too bad Java doesn't have the C# keyword "using", it would be nice to be
> able to write
>
>      void around() {
>         using (rt) {
>           proceed();
>         }
>      }

A keyword there would be nice.  But, adding keywords to a language is very hard.
  I think that about the only choice would be to make it possible for
annotations to be placed in to code.  Then we'd have a whole new dictionary of
keywords that we could play with because they'd not conflict with identifier useage.

        void around() {
                @locked( rt ) {
                        proceed();
                }
        }

this would also allow for @trylocked etc.

Gregg Wonderly
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

RE: Language extensions for java.util.concurrent?

David Holmes
In reply to this post by Dawid Kurzyniec
My 2c :-)

Dawid writes:
>   atomic int a;
>     ++a; // -> incrementAndGet()
>     a ?= 5, 8; // ->compareAndSet(5, 8)

Hmm this smacks of operator overloading! :) I don't think this sort of
syntactic shorthand buys you that much, and the messier part is the details
of passing 'a' as an argument or returning 'a' from a method. Personally I
like to see what the code actually does - eg. a.compareAndSet(...) and be
able to see just by looking at a code fragment that 'a' is not a plain old
int. I do like the ?= operator though :)

As for the more general form that Jeremy is discussing:
  atomic { ... }

I just don't buy into this sort of system magic (yet!). Languages have been
trying to sell the "tell us what you want and we'll figure out how to do it"
story on concurrency for many many years. They don't succeed because
basically noone believes they can both do it right and fast. There is no
one-size-fits-all solution for concurrency and synchronization, and until
there is people want control over how these things are done. Maybe we are
edging closer to that but I don't think we are that close yet.

The software transaction stuff is promising for some things (like replacing
synchronized with an optimistic approach that falls back to locking) but the
issue of restarts and side-effects still indicate to me that they are not a
general solution to the problem. They work well in databases but that is an
environment where there are no side-effects other than the actions of the
transaction on the database.

>     lock.lock() { // ->lock(); try { ... } finally { unlock(); }

This is a job for IDE tools, like eclipse (which I think does something like
this). Though if continuations were ever to be added to the language then
something like:
    lock.lock() { ... } -> lock.withLock(new Runnable { ... })
might be reasonable.

Aside: there have been some informal proposals for general before/after
"try" blocks over the years but they never gained enough general support to
be considered worthwhile. As Dawid alluded, exceptions and return values
make things messy.


As for Jeremy's annotation tool ... Egads man! That's not an annotation tool
it is a pre-processor! Shame on you for subverting annotations in that way.
Anyone who has read the JLS should know that annotations are not meant to
affect semantics! ;-)       :-)

Cheers,
David Holmes

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions for java.util.concurrent?

Jeremy Manson
David Holmes wrote:

> As for the more general form that Jeremy is discussing:
>   atomic { ... }
>
> I just don't buy into this sort of system magic (yet!). Languages have been
> trying to sell the "tell us what you want and we'll figure out how to do it"
> story on concurrency for many many years. They don't succeed because
> basically noone believes they can both do it right and fast. There is no
> one-size-fits-all solution for concurrency and synchronization, and until
> there is people want control over how these things are done. Maybe we are
> edging closer to that but I don't think we are that close yet.
>
> The software transaction stuff is promising for some things (like replacing
> synchronized with an optimistic approach that falls back to locking) but the
> issue of restarts and side-effects still indicate to me that they are not a
> general solution to the problem. They work well in databases but that is an
> environment where there are no side-effects other than the actions of the
> transaction on the database.

I'll go even farther and say that I don't think there will ever be a
one-size-fits-all solution for concurrency and synchronization, any more
than there is a one-size-fits-all solution for, say, control flow.  We
have if statements and do-while loops and while loops and for loops -
there is no reason that there can't be a range of flavors for
concurrency.  Given that, the goal is (or should be) to create a set of
tools that are both comprehensible and usable in a broad range of contexts.

JSRs 133 and 166 do this well, but I would still argue that creating
(say) non-blocking data structures requires serious concurrency jujitsu.
  Learning and understanding the correct use of volatile and AtomicXXXX
is hard!  I think that this is the area that development of atomic
should focus on.  And given the number of non-blocking algorithm
implementations that require at least some restart / retry, I think it
is reasonable to have that be part of it.

> As for Jeremy's annotation tool ... Egads man! That's not an annotation tool
> it is a pre-processor! Shame on you for subverting annotations in that way.
> Anyone who has read the JLS should know that annotations are not meant to
> affect semantics! ;-)       :-)

I'm so ashamed!  I should go back to my original idea, which was to use
Aspect Oriented Programming!  It is so much simpler and easier to
understand!  And the resulting code is so much easier to read!

"Annotation Tool", "Pre-processor", "Po-tay-to", "Po-tah-to".  Call it a
pre-processor if it makes you feel better.  The annotation isn't
changing the code's behavior, the bytecode rewriter is.  It just happens
to be using the annotation as a marker to decide when to do it.

                                        Jeremy
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions for java.util.concurrent?

Brian Goetz
In reply to this post by David Holmes
> I just don't buy into this sort of system magic (yet!). Languages have been
> trying to sell the "tell us what you want and we'll figure out how to do it"
> story on concurrency for many many years. They don't succeed because
> basically noone believes they can both do it right and fast.

I'm not sure that it needs to be right and fast for it to be damn useful
today.  Getting locking right is just too hard for Joe Java.
Concurrency bugs that make it into the field and fail there are very
expensive.  So for Joe Java, something that gets it right, and not
godawful slow, might still be a big improvement.  And it will get
faster, just like everything else does (synchronization was pretty slow
in Java 1.0.)


> The software transaction stuff is promising for some things (like replacing
> synchronized with an optimistic approach that falls back to locking) but the
> issue of restarts and side-effects still indicate to me that they are not a
> general solution to the problem. They work well in databases but that is an
> environment where there are no side-effects other than the actions of the
> transaction on the database.

I think the real win in the transactional stuff is that it makes writing
a concurrent algorithm nearly as easy as writing a sequential one.  The
performance of a V1.0 system might not be all that much worse than what
Joe Java cooks up.


And with respect to annotations -- turn away from the dark side, Jeremy...

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions for java.util.concurrent?

Doug Lea
In reply to this post by Jeremy Manson
This is getting increasingly removed from the usual concurrency-interest
topics, but ...

Jeremy Manson wrote:
>   Given that, the goal is (or should be) to create a set of
> tools that are both comprehensible and usable in a broad range of contexts.

So, to provide transactional alternatives/complements to locking, you'd
want to support lightweight language-based constructions that mirror the
cases in which database-style transactions tend to work best:
   1. They contain well-defined read-sets and write-sets
   2. They do not contain side-effecting actions or IO
   3. They do not contain nested transactions (or only limited forms)
   4. They are short.

Databases normally ensure (1) and (2) "syntactically" (SQL etc).
They usually deal with (3) by either dynamically disallowing nesting or
by requiring additional setup/cost. And either don't deal with (4) at
all (with occasional crummy performance or lots of aborts etc) or
support special long-transaction constructions.

It's a challenge to come up with a combination of syntax and APIs
that captures this well. I do think that this is the right challenge to
undertake though, rather than for example allowing arbitrary "atomic {}"
blocks that just so  happen to be well-behaved when constrained in the
above ways that you can't enforce or even check.

>
> JSRs 133 and 166 do this well, but I would still argue that creating
> (say) non-blocking data structures requires serious concurrency jujitsu.

The main jujitsu is in inventing useful/efficient data-structures that
require only single-location compareAndSet. Until hardware can provide
multi-location transactional updates (coincidentally, in the same sense
as that above :-), and maybe even afterwards, these pointwise
solutions that take a lot of effort to devise and implement
correctly are likely to remain best choices.

But, in the same sense that you don't want application programmers to
bother re-implementing tricky non-concurrent data structures like
red-black trees, but instead use standardized APIs like TreeMap, I think
the most promising focus here is in identifying useful
concurrency-related APIs and providing the best implementations we can
come up with. Which just so happens to be the mission of JSR166 and its
follow-ons :-) And co-exists with longer-term goals like those above of
making  transactional forms more usable and useful for other kinds of
usages.

-Doug
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions for java.util.concurrent?

Chris Purcell
Just to note,

>   1. They contain well-defined read-sets and write-sets

*Dynamically determined* read-sets and write-sets, if you use the
current state-of-the-art. I'm not sure what you mean by "well-defined".

>   3. They do not contain nested transactions (or only limited forms)

Nested atomic blocks are usually supported.

Chris

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions for java.util.concurrent?

Doug Lea
Chris Purcell wrote:
> Just to note,
>
>>   1. They contain well-defined read-sets and write-sets
>
>
> *Dynamically determined* read-sets and write-sets, if you use the
> current state-of-the-art. I'm not sure what you mean by "well-defined".

Thanks. To try to stay uncontroversial, I prefaced this with

>> cases in which database-style transactions tend to work best:

not, "work at all". Stretching this as far as possible in language-based
transactions is a good idea. But the possibilities of say, virtual
calls to objects of non-yet-loaded classes can make it fairly challenging.

-Doug
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions for java.util.concurrent?

Jeremy Manson
In reply to this post by Doug Lea
Doug Lea wrote:
> This is getting increasingly removed from the usual concurrency-interest
> topics, but ...

It's concurrent, isn't it? ;)

> Jeremy Manson wrote:
>
>>   Given that, the goal is (or should be) to create a set of tools that
>> are both comprehensible and usable in a broad range of contexts.
>
> So, to provide transactional alternatives/complements to locking, you'd
> want to support lightweight language-based constructions that mirror the
> cases in which database-style transactions tend to work best:
>   1. They contain well-defined read-sets and write-sets
>   2. They do not contain side-effecting actions or IO
>   3. They do not contain nested transactions (or only limited forms)
>   4. They are short.
>
> It's a challenge to come up with a combination of syntax and APIs
> that captures this well. I do think that this is the right challenge to
> undertake though, rather than for example allowing arbitrary "atomic {}"
> blocks that just so  happen to be well-behaved when constrained in the
> above ways that you can't enforce or even check.

My point was that atomic blocks are only one tool in the concurrent
programmer's toolbox, not that atomic blocks are applicable in every
context.  Sorry if that was unclear from the message.  It bothers me
when people try to sell atomic blocks as a panacea.

As far as the specific challenges - well, as I said in my first message,
I elided lots of detail.  I agree that these are several of the obvious
challenges / limitations when developing effective atomic sections for a
programming language.

Chris Purcell took issue with 3, which he says are usually supported:

 >   3. They do not contain nested transactions (or only limited forms)

If there are going to be atomics in use in libraries, it becomes pretty
important to support nested ones.   From my point of view, support for
nested transactions depends on what you expect them to do.  Their
semantics can get arbitrarily hairy.  IMO, it is better to keep them
simple where they occur (by making them reentrant, for example, in the
same way as synchronized blocks).

We could exchange messages for months on all four points, though, so it
might be better not to get too deeply into this.


>> JSRs 133 and 166 do this well, but I would still argue that creating
>> (say) non-blocking data structures requires serious concurrency jujitsu.
>
> The main jujitsu is in inventing useful/efficient data-structures that
> require only single-location compareAndSet.

I agree with this; it was, effectively, my point.  Structuring
algorithms around this is extremely tricky, as you know.

> Until hardware can provide
> multi-location transactional updates (coincidentally, in the same sense
> as that above :-), and maybe even afterwards, these pointwise
> solutions that take a lot of effort to devise and implement
> correctly are likely to remain best choices.

It may, in fact, take hardware support before atomic sections are useful
to a broad audience.  As for afterwards - well, if you have it, it makes
sense to be able to use it.

However, I think it is possible to use atomic sections in the meantime
for platforms for which you have well-understood constraints that make
it possible to implement them.  Cooperative systems / user-level thread
systems make implementation of atomic blocks a bit easier and more
sensible, for obvious reasons.  But then we are straying away from pure
Java...

> But, in the same sense that you don't want application programmers to
> bother re-implementing tricky non-concurrent data structures like
> red-black trees, but instead use standardized APIs like TreeMap, I think
> the most promising focus here is in identifying useful
> concurrency-related APIs and providing the best implementations we can
> come up with. Which just so happens to be the mission of JSR166 and its
> follow-ons :-) And co-exists with longer-term goals like those above of
> making  transactional forms more usable and useful for other kinds of
> usages.

I wouldn't disagree.  Obviously, your first line of resort should be to
see if there is an existing library that will fill your needs.  JSR-166
provides an excellent array of tools.

It is very easy to get carried away with shoving concurrency extensions
into the language.  I can think of several languages that suffer from
this problem.  There is a tendency to shoehorn in every feature that the
designer thinks is cool.  But I would say that in this case that there
is a hole in what the language provides (which is the ability to do
multi-location transactional updates).

                                        Jeremy
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Language extensions for java.util.concurrent?

Doug Lea
In reply to this post by Brian Goetz
Brian Goetz wrote:

> [... transactions ...]
>
>> I just don't buy into this sort of system magic (yet!). Languages
>> have been trying to sell the "tell us what you want and we'll
>> figure out how to do it" story on concurrency for many many years.
>>  They don't succeed because basically noone believes they can both
>>  do it right and fast.
>
>
> I'm not sure that it needs to be right and fast for it to be damn
> useful today.

Millions of J2EE/EJB application programmers apparently agree with you.

Recall that most J2EE application code does (in the most common usages)
behave in a basically transactional manner: Containers
may serialize objects for the sake of roll back on failure,
eliminate ability for application code to perform most IO or create  '
new threads, update most object state only via database operations, and
so on.  There's supposedly no need for these programmers to ever use
"synchronized" or anything in java.util.concurrent, and also no need to
deal with security or distribution. Although, inevitably, there are
some cases where reality leaks through, so it is hard to make
simple uniform semantic guarantees about effects.

All this middleware support has a huge effect on performance, but many
people seem to think it is worth the tradeoffs.

But even J2EE programmers sometimes find that they need or
want to introduce concurrency rather than just maintain safety in
the face of potential underlying concurrency. So, stay tuned for
the JSR236/237 "Managed" versions of the java.util.concurrent Executor
framework that will allow J2EE programmers to use async tasks and
Futures as their main (and practically only) form of concurrency
support. The in-progress JSR236/237 will be standardizing the kinds of
things now non-standardly supported in the Websphere and Weblogic
"commonj" work packages to instead use special forms of j.u.c Executors.

Anyway, my main point is just the usual one: There are lots of
useful styles of (concurrent) programming, none of which are always
best, but all of which might be subject to improvement via combinations
of better language, library, and tool support.

-Doug

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest