Skip to content

Instantly share code, notes, and snippets.

@jskorpan
Created July 8, 2011 07:41
Show Gist options
  • Select an option

  • Save jskorpan/1071323 to your computer and use it in GitHub Desktop.

Select an option

Save jskorpan/1071323 to your computer and use it in GitHub Desktop.
A ConcurrentSkipListSet based Timer compatible with Netty's HashedWheelTimer
import org.jboss.netty.util.ThreadRenamingRunnable;
import org.jboss.netty.util.Timeout;
import org.jboss.netty.util.Timer;
import org.jboss.netty.util.TimerTask;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class SortListTimer implements Timer, Runnable
{
private final SortedSet<InternalTimeout> timeouts = new ConcurrentSkipListSet<InternalTimeout>(new Comparator<InternalTimeout>() {
public int compare(InternalTimeout o1, InternalTimeout o2)
{
return (int) (o1.getExpireTick() - o2.getExpireTick());
}
});
private final int resolutionMSEC;
private boolean isRunning;
private Thread timerThread;
private class InternalTimeout implements Timeout
{
private AtomicBoolean isExpired = new AtomicBoolean();
private AtomicBoolean isCancelled = new AtomicBoolean();
private long expireTime;
private TimerTask task;
private InternalTimeout(long expireTime, TimerTask task)
{
this.expireTime = expireTime;
this.task = task;
}
public Timer getTimer()
{
return SortListTimer.this;
}
public TimerTask getTask()
{
return task;
}
public boolean isExpired()
{
return isExpired.get();
}
public boolean isCancelled()
{
return isCancelled.get();
}
public void cancel()
{
isCancelled.set(true);
}
public long getExpireTick()
{
return expireTime;
}
public void setExpired(boolean b)
{
isExpired.set(b);
}
public void incExpireTime()
{
expireTime ++;
}
}
public SortListTimer(int resolutionMSEC)
{
this.resolutionMSEC = resolutionMSEC;
this.isRunning = true;
this.timerThread = new Thread(new ThreadRenamingRunnable(this, "SortListTimer"));
timerThread.start();
}
private long getTime()
{
return System.currentTimeMillis();
}
public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit)
{
long expire = getTime() + TimeUnit.MILLISECONDS.convert(delay, unit);
InternalTimeout e = new InternalTimeout(expire, task);
while(!timeouts.add(e))
{
e.incExpireTime();
}
return e;
}
public Set<Timeout> stop()
{
isRunning = false;
try
{
timerThread.join();
} catch (InterruptedException e)
{
}
return null;
}
private void update()
{
long now = getTime();
Iterator<InternalTimeout> iterator = timeouts.iterator();
while (iterator.hasNext())
{
InternalTimeout next = iterator.next();
if (now > next.getExpireTick())
{
iterator.remove();
if (next.isCancelled())
{
continue;
}
try
{
next.setExpired(true);
next.getTask().run(next);
} catch (Throwable e)
{
e.printStackTrace();
}
}
else
{
break;
}
}
}
public void run()
{
while (isRunning)
{
update();
try
{
Thread.sleep(resolutionMSEC);
} catch (InterruptedException e)
{
}
}
}
}
@tsuna
Copy link

tsuna commented Feb 13, 2012

How does this perform compared to Netty's HashedWheelTimer?

@jskorpan
Copy link
Author

jskorpan commented Feb 13, 2012 via email

@tsuna
Copy link

tsuna commented Feb 13, 2012

Thanks for the quick reply!

Well first of all it's working and doesn't skip scheduled events as Netty's
HashedWheelTimer did back in 2011

Do you know if this bug has been fixed?

With regards to performance we're using it in production under "normal
circumstances".

Can you share how many timer insertions / removal do you typically do per second?

I suggest you load test it according to your specific use case. A possible
performance hot spot is if many tasks are scheduled at the exact same timestamp

Yeah but HashedWheelTimer would have the same hot spot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment