com.caucho.util.Alarm Maven / Gradle / Ivy
Show all versions of resin-kernel Show documentation
/*
* Copyright (c) 1998-2012 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.util;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.caucho.env.thread.AbstractTaskWorker;
import com.caucho.loader.ClassLoaderListener;
import com.caucho.loader.DynamicClassLoader;
/**
* The alarm class provides a lightweight event scheduler. This allows
* an objects to schedule a timeout without creating a new thread.
*
* A separate thread periodically tests the queue for alarms ready.
*/
public class Alarm implements ThreadTask, ClassLoaderListener {
private static final Logger log
= Logger.getLogger(Alarm.class.getName());
private static final ClassLoader _systemLoader;
private static AtomicReferenceFieldUpdater _nextUpdater;
private static AtomicLongFieldUpdater _wakeTimeUpdater;
// private static final AlarmThread _alarmThread;
private static final CoordinatorThread _coordinatorThread;
/*
private static volatile long _currentTime = System.currentTimeMillis();
private static volatile boolean _isCurrentTimeUsed;
private static volatile boolean _isSlowTime;
*/
// private static final AlarmHeap _heap = new AlarmHeap();
private static final AlarmClock _clock = new AlarmClock();
private static final AtomicInteger _runningAlarmCount
= new AtomicInteger();
private static final boolean _isStressTest;
/*
private static long _testTime;
private static long _testNanoDelta;
*/
private volatile Alarm _next;
private volatile long _wakeTime;
private AlarmListener _listener;
private ClassLoader _contextLoader;
private String _name;
private boolean _isPriority = true;
// private int _heapIndex = 0;
private int _bucket = -1;
private volatile boolean _isRunning;
/**
* Create a new wakeup alarm with a designated listener as a callback.
* The alarm is not scheduled.
*/
protected Alarm()
{
this("alarm");
}
protected Alarm(String name)
{
if (_coordinatorThread == null) {
throw new IllegalStateException("Alarm cannot be instantiated because Resin is running inside a foreign classloader."
+ "\n " + Alarm.class.getClassLoader());
}
_name = name;
addEnvironmentListener();
}
/**
* Create a new wakeup alarm with a designated listener as a callback.
* The alarm is not scheduled.
*/
public Alarm(AlarmListener listener)
{
this("alarm[" + listener + "]", listener);
}
/**
* Create a new wakeup alarm with a designated listener as a callback.
* The alarm is not scheduled.
*/
public Alarm(String name, AlarmListener listener)
{
this(name, listener, Thread.currentThread().getContextClassLoader());
}
/**
* Create a new wakeup alarm with a designated listener as a callback.
* The alarm is not scheduled.
*/
public Alarm(String name, AlarmListener listener, ClassLoader loader)
{
this(name);
setListener(listener);
setContextLoader(loader);
}
/**
* Create a new wakeup alarm with a designated listener as a callback.
* The alarm is not scheduled.
*/
public Alarm(String name,
AlarmListener listener,
long delta,
ClassLoader loader)
{
this(name);
setListener(listener);
setContextLoader(loader);
queue(delta);
}
/**
* Creates a named alarm and schedules its wakeup.
*
* @param name the object prepared to receive the callback
* @param listener the object prepared to receive the callback
* @param delta the time in milliseconds to wake up
*/
public Alarm(String name, AlarmListener listener, long delta)
{
this(name, listener);
queue(delta);
}
/**
* Creates a new alarm and schedules its wakeup.
*
* @param listener the object prepared to receive the callback
* @param delta the time in milliseconds to wake up
*/
public Alarm(AlarmListener listener, long delta)
{
this(listener);
queue(delta);
}
/**
* Returns the alarm name.
*/
public String getName()
{
return _name;
}
/**
* Sets the alarm name.
*/
protected void setName(String name)
{
_name = name;
}
/*
public static boolean isActive()
{
return _testTime == 0 && _alarmThread != null;
}
*/
/**
* Returns the approximate current time in milliseconds.
* Convenient for minimizing system calls.
*/
/*
public static long getCurrentTime()
{
// test avoids extra writes on multicore machines
if (! _isCurrentTimeUsed) {
if (_testTime > 0)
return _testTime;
if (_alarmThread == null)
return System.currentTimeMillis();
if (_isSlowTime) {
_isSlowTime = false;
_currentTime = System.currentTimeMillis();
_isCurrentTimeUsed = true;
LockSupport.unpark(_alarmThread);
}
else {
_isCurrentTimeUsed = true;
}
}
return _currentTime;
}
*/
/**
* Gets current time, handling test
*/
/*
public static long getCurrentTimeActual()
{
if (_testTime > 0)
return System.currentTimeMillis();
else
return getCurrentTime();
}
*/
/**
* Returns the exact current time in milliseconds.
*/
/*
public static long getExactTime()
{
if (_testTime > 0)
return _testTime;
else {
return System.currentTimeMillis();
}
}
*/
/**
* Returns the exact current time in nanoseconds.
*/
/*
public static long getExactTimeNanoseconds()
{
if (_testTime > 0) {
// php/190u
// System.nanoTime() is not related to currentTimeMillis(), so return
// a different offset. See System.nanoTime() javadoc
return (_testTime - 10000000) * 1000000L + _testNanoDelta;
}
return System.nanoTime();
}
*/
/**
* Returns true for testing.
*/
/*
public static boolean isTest()
{
return _testTime > 0;
}
*/
/**
* Yield if in test mode to maintain ordering
*/
/*
public static void yieldIfTest()
{
if (_testTime > 0) {
// Thread.yield();
}
}
*/
/**
* Returns the wake time of this alarm.
*/
public long getWakeTime()
{
return _wakeTime;
}
public void setWakeTime(long wakeTime)
{
_wakeTime = wakeTime;
}
boolean setWakeTime(long prevWakeTime, long wakeTime)
{
return _wakeTimeUpdater.compareAndSet(this, prevWakeTime, wakeTime);
}
long getAndSetWakeTime(long wakeTime)
{
return _wakeTimeUpdater.getAndSet(this, wakeTime);
}
int getHeapIndex()
{
// return _heapIndex;
return 0;
}
int getBucket()
{
return _bucket;
}
void setBucket(int bucket)
{
_bucket = bucket;
}
void setHeapIndex(int index)
{
// _heapIndex = index;
}
Alarm getNext()
{
return _next;
}
boolean setNext(Alarm prevNext, Alarm next)
{
return _nextUpdater.compareAndSet(this, prevNext, next);
}
void setNext(Alarm next)
{
_next = next;
}
/**
* Return the alarm's listener.
*/
public AlarmListener getListener()
{
return _listener;
}
/**
* Sets the alarm's listener.
*/
public void setListener(AlarmListener listener)
{
_listener = listener;
}
/**
* Sets the alarm's context loader
*/
public void setContextLoader(ClassLoader loader)
{
_contextLoader = loader;
}
/**
* Sets the alarm's context loader
*/
public ClassLoader getContextLoader()
{
return _contextLoader;
}
/**
* Returns true if the alarm is currently queued.
*/
public boolean isQueued()
{
// return _heapIndex != 0;
// return _wakeTime != 0;
return _bucket >= 0;
}
/**
* Returns true if the alarm is currently running
*/
boolean isRunning()
{
return _isRunning;
}
/**
* True for a priority alarm (default)
*/
public void setPriority(boolean isPriority)
{
_isPriority = isPriority;
}
/**
* True for a priority alarm (default)
*/
public boolean isPriority()
{
return _isPriority;
}
/**
* Registers the alarm with the environment listener for auto-close
*/
protected void addEnvironmentListener()
{
// Environment.addClassLoaderListener(this);
}
/**
* Queue the alarm for wakeup.
*
* @param delta time in milliseconds to wake
*/
public void queue(long delta)
{
long now = CurrentTime.getExactTime();
// boolean isNotify = _heap.queueAt(this, now + delta);
boolean isNotify = _clock.queueAt(this, now + delta);
if (isNotify) {
_coordinatorThread.wake();
}
}
/**
* Queue the alarm for wakeup.
*
* @param delta time in milliseconds to wake
*/
public void queueAt(long wakeTime)
{
// boolean isNotify = _heap.queueAt(this, wakeTime);
boolean isNotify = _clock.queueAt(this, wakeTime);
if (isNotify) {
_coordinatorThread.wake();
}
}
/**
* Remove the alarm from the wakeup queue.
*/
public void dequeue()
{
/*
if (_heapIndex > 0)
_heap.dequeue(this);
*/
_clock.dequeue(this);
}
/**
* Runs the alarm. This is only called from the worker thread.
*/
@Override
public void run()
{
try {
handleAlarm();
} catch (Throwable e) {
log.log(Level.WARNING, e.toString(), e);
} finally {
_isRunning = false;
_runningAlarmCount.decrementAndGet();
}
}
/**
* Handles the alarm.
*/
private void handleAlarm()
{
AlarmListener listener = getListener();
if (listener == null)
return;
Thread thread = Thread.currentThread();
ClassLoader loader = getContextLoader();
if (loader != null)
thread.setContextClassLoader(loader);
else
thread.setContextClassLoader(_systemLoader);
try {
listener.handleAlarm(this);
} finally {
thread.setContextClassLoader(_systemLoader);
}
}
/**
* Handles the case where a class loader has completed initialization
*/
@Override
public void classLoaderInit(DynamicClassLoader loader)
{
}
/**
* Handles the case where a class loader is dropped.
*/
@Override
public void classLoaderDestroy(DynamicClassLoader loader)
{
close();
}
/**
* Closes the alarm instance
*/
public void close()
{
dequeue();
// server/16a{0,1}
/*
_listener = null;
_contextLoader = null;
*/
}
// test
static void testClear()
{
// _heap.testClear();
_clock.testClear();
}
static void setAlarmTestTime(long time)
{
// Alarm alarm;
Thread thread = Thread.currentThread();
ClassLoader oldLoader = thread.getContextClassLoader();
try {
long now = CurrentTime.getCurrentTime();
_clock.extractAlarm(now, true);
/*
while ((alarm = _heap.extractAlarm(now)) != null) {
try {
alarm.run();
} catch (Throwable e) {
e.printStackTrace();
}
}
*/
} finally {
thread.setContextClassLoader(oldLoader);
}
}
/*
static void setTestNanoDelta(long delta)
{
_testNanoDelta = delta;
}
*/
public String toString()
{
return "Alarm[" + _name + "]";
}
/*
static class AlarmThread extends Thread {
AlarmThread()
{
super("resin-timer");
setDaemon(true);
setPriority(Thread.MAX_PRIORITY);
}
@Override
public void run()
{
int idleCount = 0;
while (true) {
try {
if (_testTime > 0) {
_currentTime = _testTime;
LockSupport.park();
continue;
}
long now = System.currentTimeMillis();
_currentTime = now;
boolean isCurrentTimeUsed = _isCurrentTimeUsed;
_isCurrentTimeUsed = false;
if (isCurrentTimeUsed) {
idleCount = 0;
_isSlowTime = false;
}
else {
idleCount++;
if (idleCount >= 10) {
_isSlowTime = true;
}
}
if (_isSlowTime) {
long sleepTime = 1000L;
LockSupport.parkNanos(sleepTime * 1000000L);
}
else {
Thread.sleep(20);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}
*/
static class CoordinatorThread extends AbstractTaskWorker {
private long _lastTime;
@Override
protected boolean isPermanent()
{
return true;
}
/**
* Runs the coordinator task.
*/
@Override
public long runTask()
{
if (CurrentTime.isTest()) {
return -1;
}
_lastTime = CurrentTime.getExactTime();
while (true) {
long now = CurrentTime.getExactTime();
long next = _clock.extractAlarm(now, false);
// long next = _heap.nextAlarmTime();
// #3548 - getCurrentTime for consistency
// long now = getCurrentTime();
if (next < 0) {
return 120000L;
}
else if (now < next) {
return Math.min(next - now, 120000L);
}
}
}
/*
private void dispatchAlarm()
{
try {
Alarm alarm;
if ((alarm = _heap.extractAlarm(getCurrentTime())) != null) {
_runningAlarmCount.incrementAndGet();
long now;
if (_isStressTest)
now = Alarm.getExactTime();
else
now = CurrentTime.getCurrentTime();
long delta = now - alarm._wakeTime;
if (delta > 10000) {
log.warning(this + " slow alarm " + alarm + " " + delta + "ms"
+ " coordinator-delta " + (now - _lastTime) + "ms");
}
else if (_isStressTest && delta > 100) {
System.out.println(this + " slow alarm " + alarm + " " + delta
+ " coordinator-delta " + (now - _lastTime) + "ms");
}
_lastTime = now;
if (alarm.isPriority())
ThreadPool.getThreadPool().schedulePriority(alarm);
else
ThreadPool.getThreadPool().schedule(alarm);
}
} catch (Exception e) {
log.log(Level.WARNING, e.toString(), e);
}
}
*/
}
static {
// AlarmThread alarmThread = null;
CoordinatorThread coordinator = null;
ClassLoader loader = Alarm.class.getClassLoader();
try {
coordinator = new CoordinatorThread();
coordinator.wake();
} catch (Throwable e) {
e.printStackTrace();
// should display for security manager issues
log.fine("Alarm not started: " + e);
}
_nextUpdater
= AtomicReferenceFieldUpdater.newUpdater(Alarm.class, Alarm.class, "_next");
_wakeTimeUpdater
= AtomicLongFieldUpdater.newUpdater(Alarm.class, "_wakeTime");
// _systemLoader = systemLoader;
_systemLoader = loader;
// _alarmThread = alarmThread;
_coordinatorThread = coordinator;
_isStressTest = System.getProperty("caucho.stress.test") != null;
}
}