Re: Are there real use cases with the Java access modes?

Gregg Wonderly via Concurrency-interest <[email protected]> Tue, 20 Jul 2021 23:03:25 -0500
Newsgroups gmane.comp.java.jsr.166-concurrency
Message-ID <[email protected]>
I have suggested that the default of non-volatile was a problematic choice, because it creates surprise behavior out of compiler optimization of non-volatile references for even simple applications.  We all can agree that we used to write C and C++ code like the following, and having an optimizer do this for us, seems awesome.

	{ 
		register cpyA = A;
		register cpyB = B;

		… do something as fast as possible with cpyA and cpyB values…
	}

My experience with this hoist optimization, early on, was a problem that I didn’t enjoy.  There were all kinds of circumstances that made it extremely difficult for me to fix my applications to work with JDK 1.5 and get the updates to all the unknown users since my jar files had been circulated without any formal distribution system.  So, I have a raw feeling about this particular “optimization” that wasn’t something I elected to use.  The JLS already described volatile keyword use, it wasn’t something that had used because I hadn’t encounter any such optimization prior to JDK 1.5 and the JMM clarifications of all the things that were underspecified before that.

I am bring this up again, because if there are new language and JVM features happening, I’d like to see if there is some kind of solution for this particular issue that would at least allow inexperienced developers as well as experienced developers maintaining broken code that they haven’t seen a problem with yet.  Because this is a pretty silent optimization and because it’s so conditional on the code around it, my simple observation is that it’s a large surprise when the status of the hoist changes when you change code around the hoist.  Logic control statements in particular that have the hoist conditionally happen creates the surprise moment for people who don’t really understand that volatile is required to make swing applications that have listener callbacks manipulating data that the main thread is using.

I have a class that I wrote a long time ago to make the AWT-only vs external-data creator details easier.  Here’s how a listener would use it to cause an action to occur when a button is pressed or menu item selected etc.

public void ActionPerformed( ActionEvent ev ) {
	new ComponentUpdateThread<DataValues>( btn1, btn2, lbl1, lbl2, lbl3, txt1, txt2, pnlA ) {
		public void setup() { // on AWT event thread
			super.setup();   // disable all controls in cons
			txt1.setText( “” );
			txt2.setText(“”);
		}
		public DataValues construct() {  // Executes on random pool thread
			try {
				DataValues v = NetworkAPI_to_getData();
				return v;
			} catch( Exception ex ) {
				reportException(ex);
			}
			return null;
		}
		public void finish() {  // on AWT event thread
			DataValues v = getResults();
			txt1.setText(v.data1);
			txt2.setText(v.data2);
			super.finish();  // Enable all components
		}
	}.start();
}

This covers the exchange of data values between threads.  It also manages the access to the AWT component hierarchy so that it’s done with only an AWT event queue thread.  However, it still doesn’t cover the issue of non-volatile vs volatile value references between threads like my loop control example.  That’s why I am talking about this again and again.  There is literally no way that simple value references can be passed between threads without eliminating the hoist.  You just cannot pass the value between threads, and this means that everyone has to understand exactly how the AWT event queue threads work and that anything that one of those “listener bits” reads or writes, which is also read or written by the main threads has to be volatile.  I am not claiming that just completely eliminating the hoist from the compiler is the only solution.   I am just trying to describe all the details that I see and have experience about this one particular language feature and how swing in particular has a common code design that can happen to developers of new applications, early in the development, that is frustrating to figure out.  And I also mentioned that the bigger issue is that this optimization is based on code structure that is far more reaching analysis than most optimizations and that implies further surprise when the optimization changes what’s executed.

Yes I’ve been around Java from the beginning of it hitting the internet. I went to the NYC meeting where we saw applets in Mosaic.  I was on the Sun developer advisory council so I got to meet a lot of different people in Sun and in the Java community who were also participating there, such as Doug.  But I am not trying to say that makes me an expert.  I still have a whole lot less knowledge that the people on this list.

I brought this up in the past and Alex and I have gone back and forth about it and he’s constantly contended that this is either not a problem or that it’s such a rare thing that it doesn’t matter.   As a long time Java proponent, I am frustrated about various things that have happened around the “Java is a server platform” mantra that many of the people pulling strings in this community and others.  I write code from the bottom to the top of the software stack.  I do nothing on the web, because I prefer native apps better and I feel like Swing had the opportunity to solve a lot of portability problems and allow UNIXish OSes to have an easy to use GUI environment.  But there were all kinds of problems around missing APIs to simplify things.  There were too many ways to get screwed over by the threading mode because there wasn’t anything explicit, like the API I showed above.  Even in code like netbeans, people were using the AWT even thread to make network connections specifically because of the non-volatile data sharing problem.  It’s really everywhere, and I don’t know how else to illustrate that this creates a really large difficulty in places where ’small’ data exchanges are frequent and everywhere.  People just don’t create large data structures and wrapper APIs that can solve this simple issue of hoisting references, because you can’t solve it simply by an API, because you still need both threads to know about some reference or use some rendezvous mechanism to exchange a value through a class wrapped reference that is managed appropriate in declaration and happens before.

Gregg


> On Jul 20, 2021, at 8:39 PM, Joe Bowbeer via Concurrency-interest <[email protected]> wrote:
> 
> Gregg,
> 
> You've been involved off-and-on in the concurrency and JMM discussions for over 20 years, if I recall correctly.
> 
> Are you proposing that an existing optimization now be disallowed? Is this a new proposal (from you), or has your argument changed? 
> 
> (For example, now you are arguing that our understanding of Java's intended audience is different than it was 20+ years ago?)
> 
> 
> On Tue, Jul 20, 2021, 12:29 PM Gregg Wonderly via Concurrency-interest <[email protected] <mailto:[email protected]>> wrote:
> Andrew, I appreciate all of the details you enumerate below about the design of Java and how concurrency with multi-threading is a primary design element.  The reference hoist has nothing to do with that though.  It has to do with optimizing the execution time of a block of code.  Yes, the analysis takes all the details of concurrent access into consideration around code paths of this thread, able to reach back into the class in some form.  But that analysis is totally based on the ‘volatile’ nature of the value, not simply on the fact that we are in a language that provides APIs that allow multi-threading to work.
> 
> I understand that the specifics seem integrated.  But my perspective is still that this reference hoist is assuming something that is so difficult to prove and variable based on what can be proved, that it’s an alarming side effect that is a surprise, more than it is a discernible behavior without extensive knowledge to all the implementation details which control when the hoist can actually occur.  Yes, that is what optimization is about, but the reality is that this optimization breaks the logic of the application and this is observable even in a debugger because of the logic change due to the hoisted read.  Other language compilers have over decades of development broken code due to mis-architected optimizations and we’ve not left them in.
> 
> The contention I am sensing revolves around the fact that this single optimization would appear to be something that everyone is relying on to solve lots of performance problems.  In days gone by of C and C++ programming, block-level, copy-of-value declarations were used as a moment to copy needed immutable references into block level values as a form of optimization of reference.  Things like
> 
> 	SomeType val;
> 	if( ( val = externVal) != null ) {
> 		…lots of references to val...
> 	}
> 
> As a general non-locking mutation scheme, copy and replace (via CAS) reference strategies are ever present in concurrent systems, to avoid locks.  There’s just so many things that are done explicitly and provide such better documentation than a compiler optimization does.
> 
> The recurring counter point over the years, has been that experienced software developers can know why this occurs and will be able to readily fix the problem.  Really, believing that as a precursor to inaction, I feel, is a pretentious attitude that has caused a lot of people to not participate in many discussions.  It speaks to the belief that one can and does know everything, and that’s the only way to be a real software developer.  I’ve tried to have this discussion for years in a productive way.  But repeatedly, we arrive at “those are not real developers and so we don’t care about their problems” or “this is not a big problem, only some ignorant want-to-be programmers have this problem” so why should anyone care about them.
> 
> It’s really a privilege for all who spend time on this level of software construction and have the knowledge to understand all the details that are discussed in this group and amongst people of this caliber of knowledge.  Declaring it somehow required and achievable by all, is problematic, and that’s what I am continuing to stress.  Software doesn’t have to appear to be magic.  The notion of least surprise (https://en.wikipedia.org/wiki/Principle_of_least_astonishment <https://en.wikipedia.org/wiki/Principle_of_least_astonishment>) is an old principle that has books written about it to try and illuminate the ideals of how we should consider system design for the users of those systems.
> 
> There are countless examples of how “training” is a requirement before performing lots of tasks in jobs and in life in general.  It’s not the training that I am complaining about.  It’s the notion that this single optimization is something that breaks software systems in non-predictable ways because it’s a large scale software system inspection, not a local, recognizable optimization that the user somehow knows they purposefully requested.  
> 
> I am go to once again cease here because I appear to be pushing buttons with my conversation mode and I am really not trying to make people made or defensive.  At some level these are hard discussions to have, I get that, but I just really sense to much defensive posturing and so I really don’t believe that the discussion can happen without creating even more walls to have to figure out how to talk through.
> 
> Gregg
> 
>> On Jul 20, 2021, at 5:41 AM, Andrew Dinn via Concurrency-interest <[email protected] <mailto:[email protected]>> wrote:
>> 
>> On 19/07/2021 22:39, Gregg Wonderly via Concurrency-interest wrote:
>>> Thanks for you comments.   I am still trying to assert that the problem is this kind of assumption about people writing the code, actually being trained software engineers.  Instead, think of them as self taught coders.  People who only every wrote “basic” or “vb” style integrations with just some knowledge that threads even exist, let alone, as in this case, knowing that the AWT event queue is involved and that there are threads (or more in the case of dialogs and other blocking actions) reaping events from the queue and dispatching them into your code.  Even the term callback or listener, for these people, doesn’t invoke any picture of “two” things working together.
>> 
>> And I am still trying to assert that we should not hobble the language implementation to cater for that category of 'programmer' (rabbit ears de rigeur). Java has been specified and implemented for use by skilled and knowledgeable professionals. That includes knowing about and having the skill to deal with the presence of multi-threading as a core element of the language, with all its attendant complexities.
>> 
>> Of course, as a Java implementor I make no assumption that all those who use Java will be skilled, knowledgeable professionals. What I do assume is that I don't have to take the concerns or failings of unskilled or ignorant 'would-be' coders into account.
>> 
>> By the way, I believe you are having your cake and eating it in the way you present your arguments here. You cited an example program as simple to understand and behaving as expected until you remove some logging code to simplify it and suddenly ... oh,. how surprising, a non-volatile access gets hoisted! At which point this simple to understand code becomes incomprehensible to the average reader.
>> 
>> In truth, if you know how to read the different versions of this code with an awareness that Java is a multi-threaded language the complexity is never absent. The surprise you describe is prima facie evidence that your posited average reader is not reading the code correctly, whether in the original or reduced version. They just think they understand it.
>> 
>> regards,
>> 
>> 
>> Andrew Dinn
>> -----------
>> Red Hat Distinguished Engineer
>> Red Hat UK Ltd
>> Registered in England and Wales under Company Registration No. 03798903
>> Directors: Michael Cunningham, Michael ("Mike") O'Neill
>> 
>> _______________________________________________
>> Concurrency-interest mailing list
>> [email protected] <mailto:[email protected]>
>> http://cs.oswego.edu/mailman/listinfo/concurrency-interest <http://cs.oswego.edu/mailman/listinfo/concurrency-interest>
> 
> _______________________________________________
> Concurrency-interest mailing list
> [email protected] <mailto:[email protected]>
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest <http://cs.oswego.edu/mailman/listinfo/concurrency-interest>
> _______________________________________________
> Concurrency-interest mailing list
> [email protected]
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest

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