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(); } }