# Notes

## About Jikes

By default the Makefile tries to use the `jikes` compiler rather than javac. That's easily changed -- just change the comments in the Makefile. jikes is much faster than javac but you may not have it on your platform (we suggest you download it though!). Note that MacOS X has an older version of `jikes` which has some bugs in its handling of `double` constants among other things. We suggest you upgrade.

## MacOS X Notes

### Java3D Does Not Run on MacOS X

This means you can't presently run MASON's 3D package on MacOS X.

Apparently Sun and Apple are having a spat about licensing: Sun wants to make money off of Java3D, but Apple's the only large-scale platform for which Java3D is not free, and my guess is that Apple doesn't think it's fair to have to pay for something that Microsoft, Linux, and Solaris are getting for nothing. Additionally, Java3D is dead in the water now. Sun has just canned its last in-house Java3D developers, and despite its promises to keep Java3D going, has a long and sordid history of dropping projects but not open-sourcing them. This is spooking people.

Nonetheless, Java3D remains the best choice for 3D work on Windows and Linux, and we're holding with it until we decide on the Alternatives listed below.

Why not use JOGL or GL4Java? Because they are not scenegraph libraries. They're graphics primitives libraries, little more than a cover for OpenGL, and are far too low-level for a simulation model builder. [For the uniniated, GL4Java is a popular library of basic OpenGL function calls in Java; JOGL is Sun's new alternative to GL4Java].

Alternatives. There are three scenegraph possibilities on the horizon. We would appreciate your comments on them and/or help in getting MASON up and running on them.

- jfreed2 is an attempt to restart development on jfree-d, an abandoned LGPL implementation of the entire Java3D spec. It runs on top of GL4Java. As of this writing, it's not running correctly on MacOS X yet.
- Further along is Xith3D, a scenegraph library that sits on top of JOGL, and runs on MacOS X among other platforms. Xith3D is "sort of" compatible with Java3D: it has more or less the same API, but with important enough changes that you can't just take Java3D stuff and run it on Xith3D unfortunately.
- Last, OpenMind is a scenegraph library that sits on top of GL4Java, and runs on MacOS X among other platforms.

We don't have any information on whether OpenMind is better or worse than Xith3D. Again, any experiences would be welcome.

### MASON does not run on MacOS 9

MacOS 9 does not support Swing or Java2D.

### Should I use Java 1.4.1 or Java 1.3.1 on MacOS X?

Usually the answer is: use Java 1.3.1 for display!!! It's much faster than Java 1.4.1 in displaying (it's hardware accelerated), and 1.4.1's GUI is quite buggy. However, if you're just running the model (no visualization), then 1.4.1 is faster because of improvements to the VM's inlining. If you've installed 1.4.1, you can still run Java 1.3.1 rather than 1.4.1 like this:

`/System/Library/Frameworks/JavaVM.framework/Versions/1.3.1/Home/bin/java ...`

You might want to `alias` java to this. Note that launching a java app or jar file from the Finder will always use the 1.4.1 version. :-(

If you do use Java 1.4.1, be sure to use the most current version available on Software Update. Earlier versions had very bad threading and compilation bugs, among other things.

### How to do fast MacOS X Graphics

- The Time and Rate panels have a hidden feature that is very useful for OS X. It's very expensive to switch to another window in OS X to do some more drawing -- thus updating the "Time" and "Rate" fields in the Console, or any other drawing in the Console, slows things down. Here's how to do the fastest drawing in MacOS X.
  1. Use the default Display 2D options (and no "Graphics Features" turned on).
  2. Close all inspectors and Empty the Inspector List.
  3. Click on the Time and Rate buttons and they'll say "Hidden" instead of the time and the frame rate; this prevents them from redrawing each time step.
  4. Click on the 2D Display so that it's in front of the other windows and is active.
- OS X draws N-by-M grids of rectangles (such as in HeatBugs) fastest when doing so using the "stretched image" method: rather than drawing each rect, we make an N-by-M pixel bitmap, poke in individual pixel values, then stretch the image over the space. Unlike Windows and X Windows, OS X doesn't appear to incur a memory penalty by doing this, probably because the images are allocated via Quartz. However, bugs in Java 1.3.1 are such that if you use this method to draw to a Snapshot or to a Movie, the rects get "fuzzed out" -- so in those cases we turn it off automatically.

### How do I get the SDK for MacOS X?

The SDK, plus the `jikes` is part of the Developer Tools; to get the latest release (at least the December 2002 tools), register and log into Apple's Apple Developer Connection. It's free. Note that even the December 2002 tools has an older version of `jikes` which has some bugs in its handling of `double` constants among other things. We suggest you upgrade to the latest version of jikes.

### Known MacOS X Platform-Specific Bugs

1. (1.3.1 only) After a few frames of QuickTime (or writing a few snapshots out), further such frames will not draw any text. See for example Tutorial5. This appears to be due to the Java VM smashing the stack when it compiles bytecode to machine code.
2. (1.3.1 only) The layers menu (the icon in the top-left corner of 2D displays) is flakey. If you select an item in the menu via dragging, it won't get selected and you'll get a spurious "ArrayIndexOutOfBoundsException", due to bugs in Apple's internal menu handling. You have to select items by clicking on them.
3. (1.3.1 only) Due to other Apple menu bugs, switching to or from the Console may produce spurious, harmless "java.lang.StackOverflowError".
4. (1.4.1 only) [Fixed in later versions Java 1.4.1] Stretched images are antialiased (ignoring our RenderingHints). This breaks stuff like HeatBugs, Tutorial3, and AntsForage.
5. (1.4.1 only) [Fixed in later versions Java 1.4.1] After 14,000 synchronizations, locks break. Manifests itself by the model hanging after a while, or in the Console refusing to close, among other oddities.
6. (1.4.1 only) When certain functions are compiled to machine code from bytecode and then inlined, especially dealing with longs and doubles, a smash-the-stack occurs which results in incorrect values passed to the functions. Apple has fixed the bug for an upcoming version of 1.4.1; we have created a workaround for the one time we know that the problem occurs.
7. (1.4.1 only) Multi-line pop-up lists are drawn with a single line. See for example the dialog which pops up when starting a Quicktime movie.
8. (1.4.1 only) Various spurious warnings can show up with a timestamp, followed by "java[2401]". They're all nonsense.
9. (1.4.1 only) Changing the size of the Display2D Options window results in the spurious warning "kCGErrorFailure : *** Parser error line 1, character position 52".

## Windows Notes

### Known Windows Platform-Specific Bugs

1. A Display2D's backdrop is often not repainted on Windows.
2. Reponses to events are very slow when the model is running full-blast; some events may even be dropped. You can often help matters by lowering the model thread priority in the console, or adding a Delay in the console.
3. Java3D is flakey on some systems: it does flashing effects every once in a while; we suspect this is due to Java's garbage collection.

## X Windows Notes

### Known X Windows Platform-Specific Bugs

1. Reponses to events are very slow when the model is running full-blast; some events may even be dropped. You can often help matters by lowering the model thread priority in the console, or adding a Delay in the console.

## Checkpointing

Checkpointing is done using Java's serialization facility. The checkpoint file is a standard Java serialized file, but has been gzipped. Only model objects are checkpointed out, not visualization objects.

If you want to checkpoint out on one platform and read in on another platform, your classes must all have the same serialversionUIDs. Different compilers will provide different serialversionUIDs to non-static inner classes (including anonymous classes) and the outer classes they are nested within. If you've got inner classes in your model code, you must provide your own hard-coded serialversionUID. See Tutorial3 for an extended discussion of this. None of this matters if you're writing and reading on the same platform (and with the same compiler).

Some Java classes cannot be checkpointed: for example, various Graphics2D classes. This presents a problem if you rely on them in your model. You may need to make custom serializations for your classes which use these classes. See the MavDemo model code for an example (particularly the Obstacle.java file).

## Multi-Threading

MASON's visualization system runs the model underneath in a separate thread. This can create race conditions for visualization tools. Here are some synchronization rules for you to follow.

How it works. The Console first calls start(), then creates the underlying play thread. After the thread dies, the Console calls finish(). Thus code in the start() and finish() methods are always executed in the plain Swing event thread.

The model thread does the following:

1. Stop if it's been asked to. Else....
2. Step the GUIState. This in turn locks on the schedule, steps GUIState's scheduleImmediate(BEFORE) stuff, steps the schedule, steps GUIState's scheduleImmediate(AFTER) stuff, then unlocks the schedule. Inside the locks, the stepping causes the model to update, and Display2Ds to:
   - Issue an asynchronous repaint() request if we're on MacOS X
   - Otherwise draw immediately to the screen
3. Set the and ticks per second. This causes asynchronous requests to update the Time and Rate fields.
4. Stop if it's been asked to. Else...
5. Block on the event queue using `SwingUtilities.invokeAndWait(a do-nothing Runnable)`. This trick causes the model thread to wait until all the Event Queue's repaints and events have gone through. The model thread should be interrupted from this block only if being requested to stop. At any rate, note that at this point in time the model thread no longer is holding onto the schedule lock. Thus if you lock on the schedule in an event in the event queue, you'll block until this point in time, then you'll go through.
6. Stop if it's been asked to. Else...
7. Sleep a little if it's been asked to. The model thread can be interrupted from this.
8. If the simulation has completed (but the thread wasn't stopped by the user), or we're at a requested breakpoint, stop thre thread. Else...
9. Go to #1.

Also, items stepped in the GUIState's "extreme" queues are done so while holding onto the schedule lock.

Thus locking on the schedule is one way to know that you're in a safe situation with regard to the underlying model thread (it's waiting for you). Additionally, you should be aware that repaints() can occur at any time.

Most event loop and repaint things should be done by acquiring the schedule lock before getting information from the model. Remember to make your locks as brief as possible.

If that's so, then how do we stop the thread from the pause or stop button? Because the play thread blocks ont he event queue at step #5, consider the situation of pausing or stopping it. The play thread has blocked. The user pressed the stop button. We try to join() the play thread. Now the event loop has blocked on the play thread and the play thread is blocking on the event loop. Deadlock! To deal with this, we repeatedly interrupt() (with a 50ms spin-wait) the play thread until it responds with an "okay, okay, I'm dying, sheesh" signal, and then we join() it.

General Locking Rules

- Your model should never do anything which could block forever or you risk deadlock.
- If you need to send information to a Swing Widget FROM anything scheduled in the GUIState or in the Schedule, you should do so asynchronously, using SwingUtilities.invokeLater(). There's no clean way to send a synchronous blocking message without risking deadlock.
- If you need to send information FROM a Swing Widget TO the model, or read model data FROM a Swing Widget method, you can do so either asynchronously, synchronously, or as-a-single-thread.
- asynchronously: load up a Steppable with copies of the information you wish to send to the model. Schedule the Steppable as a scheduleImmediate(...) in the GUIState.
- synchronously: synchronize on state.schedule, then do your code.
- as-a-single-thread: put your code inside a Runnable and pass it into the Controller's doChangeCode(...) method. Note that this is an expensive method: it pauses the model thread, runs your code, then unpauses the model thread. Do this only for code that's not in a tight loop: for example it's appropriate for code responding to a user pressing -Return- in a text field.

Your other option is to just bag it and risk the race condition. If you're just setting a boolean value or an integer or a float, you're probably safe here. If you're setting a double or a long, or anything bigger (an array? class?), you're unsafe.

## strictfp and Duplication

MASON is designed to make it as easy as possible for you to have duplicatable results, meaning that they will run the same way on MASON regardless of platform. However, because of the different implementations of operations with floating point numbers (such as +, -, /, *, %), Java operations with doubles and singles are not guaranteed to return identical results on different platforms, unless you take the following steps:

1. In all classes which use floating-point math, add the "strictfp" keyword to the class header.
2. Replace all references of `Math.foo()` with `StrictMath.foo()`

This is supposed to guarantee identical results; though some platforms still can get into trouble because they violate the spec. But it's as close as you can get.

Note that strictfp and StrictMath are slower than plain floating-point math. So we've tried to make it possible for you to choose either to use them or to not use them (they're turned off by default). If you want platform-independent duplication, the following classes in the core of the simcore library should use the strictfp keyword declaration: ``

    sim.field.continuous.Continuous2D
    sim.field.continuous.Continuous3D
    sim.field.grid.DoubleGrid2D
    sim.field.grid.DoubleGrid3D
    sim.field.grid.IntGrid2D
    sim.field.grid.IntGrid3D

Additionally, the following classes should be set to use StrictMath class methods rather than Math class methods: ``

    sim.field.grid.DoubleGrid2D
    sim.field.grid.DoubleGrid3D
    ec.util.MersenneTwisterFast

Last, you'll need to add the "strictfp" keyword to the front of all classes in your model; usually this means your `SimState` and `Steppable` subclasses. Anonymous classes can't be strictfp: you'll need to move them to declared classes instead if they contain floating point math operations and you want them to be strictfp.

## Stuff You Should Know About the Hotspot VM

Java's default heap sizes are in the 20 Megabyte range. To increase your heap size to, say, 100 Megabytes, you can do:

    java -Xmx100M -Xms100M ...

This is useful for making your graphics run faster if you're using the Stretched Image method for drawing grids of rectangles. The big thing you want to get rid of is a rapid rate of "Full" Garbage collections (ordinary collections are no big deal). You can see ordinary and "full" collections occurring with:

    java -verbose:gc ...

If you'd like to profile your code, the easiest way is to run:

    java -Xprof ...

This prints out a profiling statement for each thread after the thread terminates, showing the piggiest interpreted, compiled, and native functions.