On the train back from work today I crafted an implementation of a HashWheel (a very cool algorithm for scheduling work), and while planning my unit tests I wanted to know that when a task had fired or been cancelled that the HashWheel released the job. Ala no memory leak. So I knocked up the following few lines that I thought were cool enough to share. The best bit was; it found a memory leak that I had missed straight away! Result! :)
I also added it to a github repository where you can find the full source code, take a look at TestTools.java under the public github repository threadtesting-mosaic.
The tool waits for up to three seconds (usually less than 40 milliseconds on my laptop; if it is going to pass that is) before throwing an exception.
To use the tool, just instantiate WeakReference and call TestTools.spinUntilReleased( ref ).
public static void spinUntilReleased( final Reference ref ) {
Runtime.getRuntime().gc();
spinUntilTrue( new Predicate() {
public boolean eval() {
return ref.get() == null;
}
});
}
Here is an example unit test that makes use of this tool:
@Test
public void scheduleTask_cancelViaTicketAndLetGoOfTask_expectTaskToBeGCd() {
MyTask task1 = new MyTask();
HashWheel.Ticket ticket = hashWheel.register( 120, task1 );
ticket.cancel();
final Reference ref = new WeakReference(task1 );
task1 = null;
TestTools.spinUntilReleased( ref );
}
For completeness, here is the implementation of spinUntilTrue. Which can also be found in the threadtesting-mosaic repository.
public static void spinUntilTrue( Predicate predicate ) {
spinUntilTrue( 3000, predicate );
}
public static void spinUntilTrue( long timeoutMillis, Predicate predicate ) {
long startMillis = System.currentTimeMillis();
while ( !predicate.eval() ) {
long durationMillis = System.currentTimeMillis() - startMillis;
if ( durationMillis > timeoutMillis ) {
throw new IllegalStateException( "Timeout" );
}
Thread.yield();
}
}