Thread.sleep(). A problem. Any ideas?

I’m having trouble with Thread.sleep(). I’m telling it to sleep 16.666667 milliseconds, but it seems like every 7th-10th call it sleeps for twice as long.

Here’s the code:

public void run() {
		try {
			
			double sleepTime = 16.666667;
			while( true ) {	
				
				timeStep();
				
				int milli = (int)sleepTime;
				int nano = (int)((sleepTime - milli)*1000000);
				
				long startSleep = System.currentTimeMillis();
				Thread.sleep( milli, nano );
				long finishSleep = System.currentTimeMillis();
				
				System.out.println(finishSleep - startSleep);
				
			}
			
		}catch( InterruptedException e ) {
			//TODO:  Crash Nicely
			System.out.println("Crashed in Game Loop");
			e.printStackTrace();
			System.exit(1);
		}
	}

I’ve simplified it in some places that don’t matter. The important part is this:

long startSleep = System.currentTimeMillis();//This is debug code
Thread.sleep( milli, nano );
long finishSleep = System.currentTimeMillis();//This is debug code
				
System.out.println(finishSleep - startSleep);//this is debug code

I expect that this will print 16-17 everytime it prints. Maybe sometimes 14-18. Small variances wouldn’t make much of a difference, but huge ones do. This is the output I get:

[quote]31
16
15
16
16
15
16
16
31
15
16
16
15
16
16
15
31
16
16
15
16
16
15
16
31
16
15
16
16
15
16
31
16
15
16
[/quote]
You can see that every 10 frames or so it’s sleeping for what appears to be about twice as long. Does anyone have any idea why this is happening? I can develop a really crappy work around, but I’d rather not have to. Any ideas would be appreciated.

Thanks,

-Josh

Sleep isn’t very accurate. Take a look at lwjgl’s Display.sync method. It’s the most robust throttling scheme I know. (Well, I wrote it, but I did a lot of research and testing beforehand. It really doesn’t get any better than that.)

If my game thread isn’t directly handling rendering, can I still use Display.sync? The loop is entirely logic. There’s no graphics in there.

If you only want to throttle this one loop, yes. Otherwise it’s a bad idea, because it uses some class variables.

Well, the code is available. You can copy&paste it over if you like.

sleep is more accurate than currentTimeMillis on Windows :stuck_out_tongue: - use nanoTime in JRE 1.5 to get more reliable measurements

[quote=“irrisor,post:5,topic:31592”]
Except that nanotime can totally freak out on some cpus (some (most?) dual core amds, for example) and start jumping back and forth between two different (slightly out of phase) timers.

Negative game render time? It happens.

Didn’t they put some workarounds in for that?

Cas :slight_smile:

in some jre versions - that’s true sigh… use your stopwatch ::slight_smile:

the System.currentTimeMillis method on window have a low granularity around 16.xxx, this means that :
realtime = 1 => currentTimeMillis = 0
realtime = 2 => currentTimeMillis = 0
realtime = 10 => currentTimeMillis = 0
realtime = 15 => currentTimeMillis = 15
realtime = 17 => currentTimeMillis = 15
realtime = 20 => currentTimeMillis = 15
realtime = 25 => currentTimeMillis = 15
realtime = 30 => currentTimeMillis = 15
realtime = 32 => currentTimeMillis = 32
etc…

so if you got 0 it mean between 0 and 15
so if you got 15 it mean between 15 and 32
and etc…

Thread.sleep is more accurate but as you dont know the real time you cannot know how long you have to sleep/wait !!

below is the implementation I used wich give good results for me (maximum allowed fps == 1000/16 aprrox 62fps)

iTimer.getTime() return System.currentTimeMillis




this.fpsBuffer=new long[50];
this.maxFPS100=3000; //30 fps

thread run method:	
public void run()
{
try
{
 while(this.runProcess)
  {
   if(!this.pauseProcess)
   {
    //Make sure that we dont render twice a frame in the same timer ticks
    currentTime=this.iTimer.getTime()-this.startTime;
    while(currentTime==lastCurrentTime)
    {
     currentTime=this.iTimer.getTime()-this.startTime;
     Thread.sleep(1);
   }
    lastCurrentTime=currentTime;
    //Compute microsecond per frame using user selected FPS
    long frameTimeUs=100000000/this.maxFPS100;

    //Compute how many frame should have been rendered since start with frameTimeUs
    long nbFrameSinceStart=(1000*currentTime)/frameTimeUs;

    //Compute what should be the next frame millisecond rendering time
    long currentFrameTimeMs=((nbFrameSinceStart+1)*frameTimeUs)/1000;

    //Ensure that we dont render twice same frame
    if(lastCurrentFrameTimeMs==currentFrameTimeMs)
     currentFrameTimeMs=lastCurrentFrameTimeMs+frameTimeUs/1000;

    if((currentFrameTimeMs-lastCurrentFrameTimeMs)>(frameTimeUs/1000))
      currentFrameTimeMs=lastCurrentFrameTimeMs+frameTimeUs/1000;
					
    lastCurrentFrameTimeMs=currentFrameTimeMs;
					
    while(currentTime<currentFrameTimeMs)
   {
    overEatCPU=0;
    Thread.sleep(1);
    if(this.iTimer.getTime()-this.startTime==currentTime)
     currentTime++;
   else
    currentTime=this.iTimer.getTime()-this.startTime;
   }
					
   if(overEatCPU>1)
    Thread.yield();
						
   overEatCPU++;
   this.fpsBuffer[this.numLoop%this.fpsBuffer.length]=currentTime+startTime;
   this.render();
   this.numLoop++;						
					
   }
   else
   {
    Thread.sleep(1);
    Thread.yield();
   }
  }
 }
catch(InterruptedException ie)
{
 Log.log(ie);					
}
}

Still happens for me, both at home and at work. As for java version, it auto-updated just a few days ago.

I’ve made my timer class detect if time ever runs backwards, then revert to using average tick times instead of actual measured tick times if it ever does. Seems to work sufficiently well.

Wow. I had similiar problems with nanoTime, but i never really researched the results as it works on my own machines…
However now that i know this, I think i’ll use the method Markus_Persson’s suggested.

Thanks a lot for sharing this info all :slight_smile:

  • Scarzzurs

Yea, that’s what I’ve done too. I’ve implemented a method using nanoTime, but if I get a negative elapsed time reading, I switch to another algorithm I wrote that does pretty well at keeping up.

Thanks for the info guys. Does anyone know when Sun plans on fixing that nanoTime bug?

-Josh

It’s an OS/Processor bug, I believe.

Still, it renders nanotime pretty much useless for proper timing. There are other ways of measuring time, just not from pure java.

We seem to do ok with the LWJGL timer. No idea how it stacks up on Linux or Mac but haven’t had any reports.

Cas :slight_smile:

yeah LWJGL timer is fine (ms accuracy) but you need that nasty native lib :-\

Yeah, that one is a thing of beauty. =)
modifies post to say “pure java”

Seeing as the lib is all properly signed and available on any useful widespread platform out there… does it really matter?

Cas :slight_smile:

Well, yes, it won’t work in (unsigned, so the user doesn’t have to trust me) applets.

Regarding the AMD Dual Core nanotime bug, AMD have released a utility in August of 2007 that seams to resolve this issue:

From their website:

http://www.amd.com/us-en/Processors/TechnicalResources/0,,30_182_871_13118,00.html

DP :slight_smile: