Bizarre While() Behavior in Java

From Visual Basic to GNU C, this is the place to talk programming.

Moderators: SecretSquirrel, just brew it!

Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 1:15 am

I've run into a problem while reading Head First Java, 2nd Edition. I'm currently reading about event handlers, and in order to better illustrate how they work, the book directs the reader in the creation of a little music application that plays back midis. (Part of) The original code given in the book is as follows:

Code: Select all
         
for(int i = 5; i < 60; i+=4){
         
   track.add(makeEvent(144,1,i,100,i));
   track.add(makeEvent(176, 1, 127, 0, i));
   track.add(makeEvent(128, 1, i, 100, i + 2));
            
}
         
sequencer.setSequence(seq);
sequencer.setTempoInBPM(220);
sequencer.start();


The point is to insert and fire a ControllerEvent (which you can listen for) with each Note On. Every time a ControllerEvent is heard, "la" is printed to the screen. I have found that the code given is insufficient, because the program hangs forever after the music finishes playing. The errata for Head First Java suggests putting the main thread to sleep for a bit (so the sequencer can finish playing back), then closing the sequencer:

Code: Select all
         
sequencer.start();

Thread.sleep(1000 * 5);
         
sequencer.close();


This seemed rather ugly to me, and after browsing through the Java 6 API, I thought I had found a slightly better solution:

Code: Select all
         
sequencer.start();

while(sequencer.isRunning());
         
sequencer.close();


This is where the bizarre behavior comes in. If the while statement is empty, using while(sequencer.isRunning()); or while(sequencer.isRunning()){}, the program runs forever. BUT, if I put something in the curly braces, like while(sequencer.isRunning()){ System.out.print("");), it works perfectly.

If the while loop is empty, it doesn't work.

If the while has something in it, even a print() statement that prints nothing, it works perfectly.

This...doesn't make any sense to me. I've tried disassembling the bytecode using javap -c with both an empty while loop and a while loop containing print(), and I can't see a significant difference between them.

Am I missing something obvious?
Under Construction Forever~~~
Kurotetsu
Gerbil Elite
 
Posts: 528
Joined: Sun Dec 09, 2007 12:13 pm

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 2:56 am

The while loop creates what is known as a busy wait or spinwait. Since the empty while loop has nothing to complete, its execution finishes incredibly fast, so fast that it ends up starving all the other threads of CPU time the because scheduler has no opportunity to context switch it out, meaning that the sequencer thread will never get a chance to run. When you have something to execute inside, the scheduler will have opportunities to let the sequencer thread run, thus allowing the program to complete.

What you just encountered is known as a race condition. Have fun learning about multi-threading :D

Edit: Grammar.
Last edited by Zoomastigophora on Sat Jan 22, 2011 3:36 am, edited 1 time in total.
Zoomastigophora
Gerbil Elite
 
Posts: 621
Joined: Tue Nov 11, 2008 7:10 pm

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 3:18 am

A simple but dirty solution is to sleep in the loop: while (sequencer.isRunning()) { Thread.sleep(10l) }. A cleaner solution is to add an event listener using Sequencer.addMetaEventListener() and communicate with your main thread using Object.wait() and Object.notify().
Ouroboros
Gerbil In Training
 
Posts: 8
Joined: Sat Jan 22, 2011 2:48 am

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 3:44 am

Zoomastigophora wrote:The while loop creates what is known as a busy wait or spinwait. Since the empty while loop has nothing to complete, its execution finishes incredibly fast, so fast that it ends up starving all the other threads of CPU time the because scheduler has no opportunity to context switch it out, meaning that the sequencer thread will never get a chance to run. When you have something to execute inside, the scheduler will have opportunities to let the sequencer thread run, thus allowing the program to complete.

What you just encountered is known as a race condition. Have fun learning about multi-threading :D

Edit: Grammar.

This is a little imprecise but basically correct. To elaborate a bit, the reason your print statement makes it work is because IO of that sort is actually pretty slow, and there is some downtime while that function call waits for things to happen that doesn't occur if you just keep looking to see if the sequencer is done.
Core i7 920, 3x2GB Corsair DDR3 1600, 80GB X25-M, 1TB WD Caviar Black, MSI X58 Pro-E, Radeon 4890, Cooler Master iGreen 600, Antec P183, opticals
SNM
Emperor Gerbilius I
 
Posts: 6206
Joined: Fri Dec 30, 2005 10:37 am

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 8:12 am

The behavior is still a little puzzling. I guess Java isn't using the OS's thread scheduler, and is doing its own (slightly brain-dead) timeslicing within the Java bytecode interpreter instead? Even with the empty while loop body, other background threads should still be allowed some CPU time.

But yeah, spin-waits like this are generally a bad idea, as they drive CPU usage way up. Either use some sort of event mechanism to signal the waiting thread, or (at the very least) insert a timed sleep.
(this space intentionally left blank)
just brew it!
Administrator
Gold subscriber
 
 
Posts: 38088
Joined: Tue Aug 20, 2002 10:51 pm
Location: Somewhere, having a beer

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 8:57 am

I would usually put a sleep(0) call in a loop like that, so the thread should yield the rest of the timeslice, and gets another timeslice if no other thread is waiting.

A better solution is to have an event fired when whatever you are waiting for ends, but I don't know the Java syntax.

And yes, Java must be doing something weird with the thread scheduling.
mboza
Gerbil Team Leader
 
Posts: 203
Joined: Fri Sep 15, 2006 6:52 am

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 9:07 am

Good message thread. I learn something new every day. Today, it was in Java.
If there is one thing a remote-controlled, silent and unseeable surveillance/killing machine needs, it’s more whimsy. -- Marcus
Darkmage
Darth Gerbil
Gold subscriber
 
 
Posts: 7393
Joined: Sat Mar 13, 2004 9:44 am
Location: Hell, Virginia

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 9:16 am

mboza wrote:I would usually put a sleep(0) call in a loop like that, so the thread should yield the rest of the timeslice, and gets another timeslice if no other thread is waiting.

While this is indeed the defined behavior for the Windows Sleep() API call, I don't see anything in the documentation for the Linux sleep() system call to indicate that it behaves similarly. On Linux I believe the defined way to give up your timeslice is to call sched_yield().

I also have no idea whether Java's implementation maps directly to the underlying system calls or not.

This technique is also problematic because threads (or processes) with a lower priority can still be starved for CPU time. The thread doing the Sleep(0) -- or sched_yield() -- will typically get rescheduled to run again before the lower priority threads have received a timeslice. I'll grant you that on a modern (multi-core) CPU you probably won't notice (since the lower-priority threads will still be able to run on other cores), but you're still wasting a buttload of CPU cycles doing nothing (and if you get enough high priority threads doing this you will still cause your lower priority threads to grind to a halt).

Which brings up an interesting thought: Maybe Java has created the background thread at a lower priority. That would explain why the spin-wait prevents the program from completing.
(this space intentionally left blank)
just brew it!
Administrator
Gold subscriber
 
 
Posts: 38088
Joined: Tue Aug 20, 2002 10:51 pm
Location: Somewhere, having a beer

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 9:45 am

Zoomastigophora wrote:The while loop creates what is known as a busy wait or spinwait. Since the empty while loop has nothing to complete, its execution finishes incredibly fast, so fast that it ends up starving all the other threads of CPU time the because scheduler has no opportunity to context switch it out, meaning that the sequencer thread will never get a chance to run. When you have something to execute inside, the scheduler will have opportunities to let the sequencer thread run, thus allowing the program to complete.

What you just encountered is known as a race condition. Have fun learning about multi-threading :D

Edit: Grammar.


Ummm..... no. A program in a busy wait will not starve out other processes. It will consume all CPU time available to it, but that will only be CPU time that it is allocated by the scheduler. If this weren't the case, a process going into a busy wait loop would effectively cause a denial of service for the system. I suppose if the JVM handles thread scheduling internally with only process level scheduling making it back to the kernel, you could get in this situation. However that would only be possible it the jvm thread scheduler was horrible and utterly broken.

Nor is it a race condition:

[quote=wikidedia]A race condition or race hazard is a flaw in an electronic system or process whereby the output and/or result of the process is unexpectedly and critically dependent on the sequence or timing of other events. The term originates with the idea of two signals racing each other to influence the output first.[/quote]

--SS
SecretSquirrel
Gerbil Jedi
Gold subscriber
 
 
Posts: 1737
Joined: Tue Jan 01, 2002 7:00 pm
Location: The Colony, TX (Dallas suburb)

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 10:04 am

SecretSquirrel wrote:Ummm..... no. A program in a busy wait will not starve out other processes. It will consume all CPU time available to it, but that will only be CPU time that it is allocated by the scheduler. If this weren't the case, a process going into a busy wait loop would effectively cause a denial of service for the system.

I have seen the Windows scheduler (2K/XP, not sure about Vista/7) behave very badly when there are processes with different priority classes involved. You can indeed completely starve out processes and threads of lower priority, especially on single-core systems.

On Linux I believe the scheduler guarantees that anything with a priority above "Idle" will always get at least some CPU time, so it is less of a concern (but still wastes CPU time and hurts system performance).
(this space intentionally left blank)
just brew it!
Administrator
Gold subscriber
 
 
Posts: 38088
Joined: Tue Aug 20, 2002 10:51 pm
Location: Somewhere, having a beer

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 10:45 am

Zoomastigophora wrote:The while loop creates what is known as a busy wait or spinwait. Since the empty while loop has nothing to complete, its execution finishes incredibly fast, so fast that it ends up starving all the other threads of CPU time the because scheduler has no opportunity to context switch it out, meaning that the sequencer thread will never get a chance to run. When you have something to execute inside, the scheduler will have opportunities to let the sequencer thread run, thus allowing the program to complete.

What you just encountered is known as a race condition. Have fun learning about multi-threading :D

Edit: Grammar.


I neglected to mention that the sequencer starts and DOES finish playing its midis (and the "la"s finish printing to screen), its just that it never hands back control to the main thread (I'm guessing). Before, when there was nothing after start(), it was probably because there was nothing to tell the sequencer to stop and hand back control. Now, going by what you're saying, its because it never has a chance to because of the constant isRunning() checks?

But if the sequencer finishes (and it does, the point where it finishes is the same when the program works and when it doesn't), shouldn't one of those isRunning() checks return 'false' and break out of the while loop? Or am I misunderstanding how the race condition works?
Last edited by Kurotetsu on Sat Jan 22, 2011 11:01 am, edited 1 time in total.
Under Construction Forever~~~
Kurotetsu
Gerbil Elite
 
Posts: 528
Joined: Sun Dec 09, 2007 12:13 pm

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 10:50 am

It seems fairly clear that Java is behaving badly here; but using a spin-wait like that is very sloppy. So basically, between you and Java you've got a cooperative screwup.
(this space intentionally left blank)
just brew it!
Administrator
Gold subscriber
 
 
Posts: 38088
Joined: Tue Aug 20, 2002 10:51 pm
Location: Somewhere, having a beer

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 11:34 am

just brew it! wrote:It seems fairly clear that Java is behaving badly here; but using a spin-wait like that is very sloppy. So basically, between you and Java you've got a cooperative screwup.


What I'm taking away from this is that I need to make better use of event handling or, at the very least, just use Thread.sleep() since it doesn't waste CPU cycle like a spin-wait does.

EDIT:

Which...is exactly what you said in your first post in this thread! Hah!
Under Construction Forever~~~
Kurotetsu
Gerbil Elite
 
Posts: 528
Joined: Sun Dec 09, 2007 12:13 pm

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 12:25 pm

Kurotetsu wrote:I neglected to mention that the sequencer starts and DOES finish playing its midis (and the "la"s finish printing to screen)

Oh, how interesting. Does anybody know if the JVM caches loop lookup results or something?
Core i7 920, 3x2GB Corsair DDR3 1600, 80GB X25-M, 1TB WD Caviar Black, MSI X58 Pro-E, Radeon 4890, Cooler Master iGreen 600, Antec P183, opticals
SNM
Emperor Gerbilius I
 
Posts: 6206
Joined: Fri Dec 30, 2005 10:37 am

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 1:41 pm

SecretSquirrel wrote:Ummm..... no. A program in a busy wait will not starve out other processes. It will consume all CPU time available to it, but that will only be CPU time that it is allocated by the scheduler. If this weren't the case, a process going into a busy wait loop would effectively cause a denial of service for the system. I suppose if the JVM handles thread scheduling internally with only process level scheduling making it back to the kernel, you could get in this situation. However that would only be possible it the jvm thread scheduler was horrible and utterly broken.

Nor is it a race condition:

Wikipedia wrote:A race condition or race hazard is a flaw in an electronic system or process whereby the output and/or result of the process is unexpectedly and critically dependent on the sequence or timing of other events. The term originates with the idea of two signals racing each other to influence the output first.


--SS

Java handles thread scheduling within the JVM and not at the operating system level to avoid misbehaving threads causing system performance issues, IIRC, and only uses underlying system calls to implement things such as locks and atomic operations. And Kurotetsu initial description of the problem seemed like a race condition to me; the completion of the program (the result) was dependent upon the order the sequencer thread and main threads were timesliced, although in this case, it sounds like the scheduler is causing the issue rather than the program itself.

SNM wrote:Oh, how interesting. Does anybody know if the JVM caches loop lookup results or something?

I believe the JIT compiler does since this is a classic optimization trick. The loop variable would have to be declared volatile for it to not to be cached amongst threads. I don't know what it does when the loop control is dependent upon a function call, but my instinct is that it wouldn't.

Kurotetsu wrote:What I'm taking away from this is that I need to make better use of event handling or, at the very least, just use Thread.sleep() since it doesn't waste CPU cycle like a spin-wait does.

EDIT:

Which...is exactly what you said in your first post in this thread! Hah!

Yup, basically. A busy wait is technically a way to handle this situation of waiting for another thread to do something, but it seems like there's something wonky going on with the JVM scheduler causing the busy wait to prevent the sequencer thread from setting its state to no longer running.
Zoomastigophora
Gerbil Elite
 
Posts: 621
Joined: Tue Nov 11, 2008 7:10 pm

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 1:43 pm

Sounds like you may be violating Java memory model rules with concurrent modifications to shared object fields.

What does isRunning do? What is the definition of sequencer? If it reads a field on an object and that field was modified in another thread, you need to either make the field volatile or use a wait/notify/synchronized (or another similar Java concurrency primitive in util. concurrent) to partially constrain the ordering of events in different threads and allow the modification to become visible to other threads. Otherwise the compiler is allowed to optimize based on code sequences visible in a single thread assuming no concurrent modification.

If you have an object foo, and two threads, T1 and T2:

T1:
while(!foo.boolField); // spin

T2:
// do some stuff for a few minutes
foo.boolField = true; // unblock T1

If boolField isn't volatile, the compiler can optimize that direct read of foo.boolField to load once and spin on a register, because the compiler can statically see that there's nothing IN the loop body that affects its value. That means it'll never see the write from the other thread. If you call a function in the body, it potentially has to be more conservative in the analysis of what is modified and what isn't and may reload the predicate on each iteration.

This is easy to run into in many languages when you have concurrent code or asynchronous events.
bitvector
Grand Gerbil Poohbah
 
Posts: 3234
Joined: Wed Jun 22, 2005 4:39 pm
Location: Mountain View, CA

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 2:12 pm

Zoomastigophora wrote:
SecretSquirrel wrote:Ummm..... no. A program in a busy wait will not starve out other processes. It will consume all CPU time available to it, but that will only be CPU time that it is allocated by the scheduler. If this weren't the case, a process going into a busy wait loop would effectively cause a denial of service for the system. I suppose if the JVM handles thread scheduling internally with only process level scheduling making it back to the kernel, you could get in this situation. However that would only be possible it the jvm thread scheduler was horrible and utterly broken.

Nor is it a race condition:

Wikipedia wrote:A race condition or race hazard is a flaw in an electronic system or process whereby the output and/or result of the process is unexpectedly and critically dependent on the sequence or timing of other events. The term originates with the idea of two signals racing each other to influence the output first.


--SS

Java handles thread scheduling within the JVM and not at the operating system level to avoid misbehaving threads causing system performance issues, IIRC, and only uses underlying system calls to implement things such as locks and atomic operations. And Kurotetsu initial description of the problem seemed like a race condition to me; the completion of the program (the result) was dependent upon the order the sequencer thread and main threads were timesliced, although in this case, it sounds like the scheduler is causing the issue rather than the program itself.


Yeah, I was thinking about this while running errands. It the main thread takes a lock on the shared data that .isrunning() accesses, then releases it after the call, the scheduler somehow or another never schedules the sequence except then the main thread has the shared data lock, then the sequencer would never be able to clear the running flag as it would always see the data locked by the main thread. That would actually be a problem with the isrunning() call as there is no reason for it to take a lock.
SecretSquirrel
Gerbil Jedi
Gold subscriber
 
 
Posts: 1737
Joined: Tue Jan 01, 2002 7:00 pm
Location: The Colony, TX (Dallas suburb)

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 2:20 pm

bitvector wrote:Sounds like you may be violating Java memory model rules with concurrent modifications to shared object fields.

What does isRunning do? What is the definition of sequencer?


The sequencer is just a Sequencer object (created using MidiSystem.getSequencer()):

http://download.oracle.com/javase/6/doc ... encer.html

isRunning() is defined within the class:

http://download.oracle.com/javase/6/doc ... ning%28%29

If it reads a field on an object and that field was modified in another thread, you need to either make the field volatile or use a wait/notify/synchronized (or another similar Java concurrency primitive in util. concurrent) to partially constrain the ordering of events in different threads and allow the modification to become visible to other threads. Otherwise the compiler is allowed to optimize based on code sequences visible in a single thread assuming no concurrent modification.

If you have an object foo, and two threads, T1 and T2:

T1:
while(!foo.boolField); // spin

T2:
// do some stuff for a few minutes
foo.boolField = true; // unblock T1

If boolField isn't volatile, the compiler can optimize that direct read of foo.boolField to load once and spin on a register, because the compiler can statically see that there's nothing IN the loop body that affects its value. That means it'll never see the write from the other thread. If you call a function in the body, it potentially has to be more conservative in the analysis of what is modified and what isn't and may reload the predicate on each iteration.

This is easy to run into in many languages when you have concurrent code or asynchronous events.


Veeeery interesting. I'm still a few chapters away from the multi-threading chapter of the book, but your description makes sense to me already.
Under Construction Forever~~~
Kurotetsu
Gerbil Elite
 
Posts: 528
Joined: Sun Dec 09, 2007 12:13 pm

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 2:52 pm

MidiSystem.getSequencer() returns an instance of RealTimeSequencer, which implements isRunning essentially as follows: private boolean running; public boolean isRunning () { return running; }. You can try declaring RealTimeSequencer.running volatile, but the correct solution is to use Sequencer.addMetaEventListener().
Ouroboros
Gerbil In Training
 
Posts: 8
Joined: Sat Jan 22, 2011 2:48 am

Re: Bizarre While() Behavior in Java

Postposted on Sat Jan 22, 2011 3:56 pm

Kurotetsu wrote:Veeeery interesting. I'm still a few chapters away from the multi-threading chapter of the book, but your description makes sense to me already.

Oh, you gavent done threading at all yet?
I'm less familiar with Jaba's threading model than I should be/used to be, but you're hitting all kinds of issues in the sample code provided that I guess your text is papering over for the purposes of the activity. This is probably why the author uses such an obvious hack in hardcoding a wait time. I bet subsequent activities will have you improving the code you used here once you understand what's actually going on. :)
Core i7 920, 3x2GB Corsair DDR3 1600, 80GB X25-M, 1TB WD Caviar Black, MSI X58 Pro-E, Radeon 4890, Cooler Master iGreen 600, Antec P183, opticals
SNM
Emperor Gerbilius I
 
Posts: 6206
Joined: Fri Dec 30, 2005 10:37 am

Re: Bizarre While() Behavior in Java

Postposted on Sun Jan 23, 2011 5:04 am

bitvector, do you happen to know how a compiler would treat an inspector that controls a loop? It doesn't seem like there would be any consistent way to optimize away the function call, unless it was recognizing the fact that the inspector was returning a member variable and caching that, but that seems incorrect. I don't have much of a background in compiler theory and optimization, and since my university doesn't offer any classes on compilers anymore, I don't have much time to learn about it on my own :(
Zoomastigophora
Gerbil Elite
 
Posts: 621
Joined: Tue Nov 11, 2008 7:10 pm

Re: Bizarre While() Behavior in Java

Postposted on Mon Jan 24, 2011 1:16 am

Zoomastigophora wrote:bitvector, do you happen to know how a compiler would treat an inspector that controls a loop? It doesn't seem like there would be any consistent way to optimize away the function call, unless it was recognizing the fact that the inspector was returning a member variable and caching that, but that seems incorrect.

I'm not sure what you mean by inspector, but if you mean an accessor/getter, a compiler can inline a simple "return this.field" call to a direct read of that field. Or even without inlining, you can use reachability analysis to see that method X only touches certain fields and nothing else. Then you check the intersection of that with what the loop body uses to see whether the predicate could change during the loop. Oftentimes a compiler does that same analysis on a per-statement (or expression) level and moves anything that doesn't change out of the loop -- this is called loop-invariant code motion. Now, when I say "a compiler", I don't always mean javac: there are often several levels of compilation in Java, including potentially several stages of JIT compilation at runtime*. The JIT typically has even more information than the first static compiler does.

* The JVM/javac are pretty good at inlining accessors. Final accessors can trivially be inlined in upfront compilation, and the first stage Hotspot JIT can inline most non-final accessors of a class by noting if the class loader has seen any subclasses loaded. Later, if a subclass is loaded that makes inlining invalid, the JIT trashes the existing code and recompiles it.
bitvector
Grand Gerbil Poohbah
 
Posts: 3234
Joined: Wed Jun 22, 2005 4:39 pm
Location: Mountain View, CA


Return to Developer's Den

Who is online

Users browsing this forum: No registered users and 1 guest