Native hi-rez timer for Mac

  1. Grab these 3 classes (slightly modified) from the book “Java 1.4 Game Programming”:

// File: BaseClock.java
// Description: Define a clock’s capabilities
// From: Chapter 12, “Java 1.4 Game Programming”
// by Mulholland & Murphy, ISBN 1-55622-963-1

public abstract class BaseClock
{
public abstract long getTime();
public abstract int getDefaultUnit();
public abstract void stampTime();
public abstract long getElapsedTime();
public abstract long getElapsedTime(int unit);

  public long stampTime;

  public static final int UNIT_SECONDS =          1;      //      10^0
  public static final int UNIT_MILLIS  =       1000;      //      10^3
  public static final int UNIT_MICROS  =    1000000;      //      10^6
  public static final int UNIT_NANOS   = 1000000000;      //      10^9

}


// File: StandardClock.java
// Description: A standard pure Java clock
// From: Chapter 12, “Java 1.4 Game Programming”
// by Mulholland & Murphy, ISBN 1-55622-963-1

public class StandardClock extends BaseClock
{
public long getTime()
{
return System.currentTimeMillis();
}

  public int getDefaultUnit()
  {
        return UNIT_MILLIS;
  }

  public void stampTime()
  {
        stampTime = System.currentTimeMillis();
  }

  public long getElapsedTime()
  {
        return System.currentTimeMillis() - stampTime;
  }

  public long getElapsedTime(int unit)
  {
        return ((System.currentTimeMillis() - stampTime) * unit) / UNIT_MILLIS;
  }

}


// File: HiresTimeExample.java
// Description: Test hi-rez native clocks
// From: Chapter 12, “Java 1.4 Game Programming”
// by Mulholland & Murphy, ISBN 1-55622-963-1

public class HiresTimeExample
{
public static void main(String[] args)
{
BaseClock clock = null;

// if ( NativeWinClock.isAvailable() )
if ( NativeMacClock.isAvailable() )
{
System.out.println(“Using native clock”);
// clock = new NativeWinClock();
clock = new NativeMacClock();
}
else
{
System.out.println(“Using standard clock”);
clock = new StandardClock();
}

        // Constant velocity vector (u,v) = (dx/dt, dy/dt)
        // is defined in pixels per microsecond.
        // Magnitudes 160 and 240 represent pixels per second.      
        double u = (double)160 / BaseClock.UNIT_MICROS;
        double v = (double)240 / BaseClock.UNIT_MICROS;

        // current position to update with movement
        double x = 0;
        double y = 0;

        long dt;      // elapsed time

        long counter = 0L;
        int secondsCounter = 0;

        clock.stampTime();

        while (secondsCounter<10) // if not passed 10 seconds
        {
              // get elasped time
              dt = clock.getElapsedTime(BaseClock.UNIT_MICROS);

              // stamp clock
              clock.stampTime();

              // increase elasped time counter
              counter += dt;

              // update world (move sprite)
              x += (u * dt);
              y += (v * dt);

              if (counter >= BaseClock.UNIT_MICROS) // if 1 second has elapsed
              {
                    System.out.println("Time (seconds): " + clock.getTime());
                    System.out.println("Elapsed time counter = " + counter);
                    counter -= BaseClock.UNIT_MICROS;
                    System.out.println("Pos = " + x + ", " + y);
                    secondsCounter++;
              }

              // Note: sleep() is not high-resolution,
              //       it is a source of timing error.
              try
              {
                    Thread.sleep(5);
              }
              catch (Exception e) {}
        }
  }

}

  1. Write a Java program ‘NativeMacClock.java’ (similar to ‘NativeWinClock.java’) which calls the native timer:

// File: NativeMacClock.java
// Author: Craig A. Mattocks
// Date: March 2003
// Description: Native hi-rez Apple Macintosh clock
// Reference: Chapter 12, “Java 1.4 Game Programming”
// by Mulholland & Murphy, ISBN 1-55622-963-1

public class NativeMacClock extends BaseClock
{
// JNI timer method. Keyword ‘native’ --> C function.
// Method declaration only, implementation is in file ‘MacClock.c’
private native long getCounter();

  // Global class instance vars
  private long frequency;
  private static boolean available;

  // Constructor
  public NativeMacClock()
  {
        frequency = UNIT_NANOS;      // Macintosh clock has nanosecond precision
  }

  // Class methods
  public long getTime()
  {
        return getCounter() / frequency;      // Time returned in seconds
  }

  public int getDefaultUnit()
  {
        return UNIT_SECONDS;
  }

  public void stampTime()
  {
        stampTime = getCounter();

// System.out.println("Time stamp: " + stampTime); // Verify precision
}

  public long getElapsedTime()
  {
        return ((getCounter() - stampTime) * UNIT_MILLIS) / frequency;
  }

  public long getElapsedTime(int unit)
  {
        return ((getCounter() - stampTime) * unit) / frequency;
  }

  public static boolean isAvailable()
  {
        return available;
  }

  // Static initializer executes once, when
  // class is loaded by runtime system.
  // Loads compiled C shared library:  libMacClock.jnilib (Mac OS X)
  static
  {
        try
        {
              System.loadLibrary("MacClock");
              available = true;
        }
        catch (UnsatisfiedLinkError ulp) {}
        catch (SecurityException sex)       {}
  }

}

  1. Compile the Java class that calls the native method:

javac -O NativeMacClock.java

  1. Generate a header file for the native method using javah with the native interface flag -jni:

javah -jni NativeMacClock

  1. Examine the header file and grab the JNIEXPORT signature for the native method:

more NativeMacClock.h

/* DO NOT EDIT THIS FILE - it is machine generated /
#include <jni.h>
/
Header for class NativeMacClock */

#ifndef _Included_NativeMacClock
#define _Included_NativeMacClock
#ifdef __cplusplus
extern “C” {
#endif
#undef NativeMacClock_UNIT_SECONDS
#define NativeMacClock_UNIT_SECONDS 1L
#undef NativeMacClock_UNIT_MILLIS
#define NativeMacClock_UNIT_MILLIS 1000L
#undef NativeMacClock_UNIT_MICROS
#define NativeMacClock_UNIT_MICROS 1000000L
#undef NativeMacClock_UNIT_NANOS
#define NativeMacClock_UNIT_NANOS 1000000000L
/* Inaccessible static: available /
/

  • Class: NativeMacClock
  • Method: getCounter
  • Signature: ()J
    */
    JNIEXPORT jlong JNICALL Java_NativeMacClock_getCounter
    (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

  1. Create the C source file ‘MacClock.c’ which contains the native timer method. Include the header and insert the above JNIEXPORT method signature (with appropriate parameters!):

// File: MacClock.c
// Author: Craig A. Mattocks
// Date: March 2003
// Description: Native hi-rez Apple Macintosh timer
// Returns elapsed time in nanoseconds
// since the machine was last booted.

#include <Carbon/Carbon.h>
#include “NativeMacClock.h”

JNIEXPORT jlong JNICALL Java_NativeMacClock_getCounter(JNIEnv *env, jobject this)
{
union
{
Nanoseconds ns;
UInt64 i;
} time;

  time.ns = AbsoluteToNanoseconds( UpTime() );
  return time.i;

}

  1. Compile the header and implementation files, building the JNI library as a bundle:

cc -bundle -I/System/Library/Frameworks/JavaVM.framework/Headers -o libMacClock.jnilib -framework JavaVM -framework CoreServices MacClock.c

  1. Move the JNI library to an appropriate place (optional):

mv libMacClock.jnilib /Library/Java/Extensions/

then let Java know where it can be found, either by using the:

-Djava.library.path="/Library/Java/Extensions"

option on the command line or in your information property list, or by setting the environment variables:

setenv DYLD_LIBRARY_PATH .:/Library/Java/Extensions:/usr/local/lib
setenv LD_LIBRARY_PATH .:/Library/Java/Extensions

  1. Compile and run the timer example:

javac -O HiresTimeExample.java
java HiresTimeExample

Hooray, it works!
Craig

Heres a BIG BIG BIG question:

Is there a way, using Java Web Start, to be able to deploy this hires timer to a mac client?

Moreover, can we deploy one for Windows and one for Mac without worrying about which platform is which?

Web Start actually has explicit support for native libraries. Here’s a description of how to do it:

http://java.sun.com/products/javawebstart/1.2/docs/developersguide.html#servlet

That being said, you don’t need a native high res timer on the Mac unless you are doing something truely time-critical, requiring nano-second precision. System.currentTimeMillis() on the Mac, gives 1 beat per millisecond which allows you a 1/1000th of a second resolution. Windows is of course still a problem, but you can use a timer like (WARNING! Plug in for my stuff ahead! ;D) the GAGETimer (http://java.dnsalias.com) to handle cross platform issues.

jbanes, if I am right you should probably mention in the Shared Code thread that GAGETimer 1.0 is the same or supersedes the one previously posted.

Good point. I’ll go do that. :slight_smile:

As has been pointed out naitve code with Web start is generally not a problem. But I came a cross one HUGE issue with it that prevented me from using it for a project.

I needed to install some native code… libraries that depended on other libraries that I was distributing… Some were media codecs from 3rd parties… I tried to use the mechanisms in webstart to install extensions and add paths to runtime so it could find libraries… and that API did not work. It seems if you have that kind of extension you MUST install stuff in a place that is already in the path because the webstart API is broken. (this was on Windows)

Also there seems to be no way to change the ccurrent directory in Java… even from native code… so I had no way of getting libraries into my path.

(I know of tricks to load libraries if you use A and A needs B, just loadLibrary B first and then load A… but that hack wouldn’t work in my situation. )

Btw, this has nothing to do with timers, sorry :slight_smile:

You should statically link all the libs into one. That’ll take care of the problem.

That’s not possible… the libraries are from 3rd party codecs, and they are dynamically loaded by design of those libraries. I can’t change that.

Well, that leaves you one option. Your web start program should first run a bootloader that extracts the files into the user’s path (i.e. c:\winnt\system32) before attempting to load the DLL.

That isn’t acceptable either… I don’t want to be one of those evil programmers that pollutes system32 :slight_smile:

Seriously… there is a whole directory hierarchy for this stuff that has to be installed and trying to plunk that in system32 isn’t going to work.

What I really need to do is to change the current directory. So that file opens are relative to a directory structure that I install. Something that seems to be impossible with java.

I tried using native code to change the current dir… since bizarrely there is no Java API to do it!! It did not work… it was like the JVM changed it back on me before my native code returned or something. Maybe something about preserving the current dir for the classpath???

I think this would all work if the WebStart API worked as advertised., I submitted a bug report months ago but have heard nothing.

Actually as of yet I see no need for a high res timer for mac… if you do some simple tests currentTimeMillis IS accurate to the millisecond. If you want higher resolution, check out the UNIX command gettimeofday (also available in MACOSX). It will give you accuracy up to the microsecond. Just some simple JNI code required.

  • Tristan