All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.caucho.util.AlarmClock Maven / Gradle / Ivy

There is a newer version: 4.0.66
Show newest version
/*
 * Copyright (c) 1998-2018 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.ArrayList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;

import com.caucho.env.thread.ThreadPool;

/**
 * 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 AlarmClock { private static final Logger log = Logger.getLogger(AlarmClock.class.getName()); private static final int CLOCK_PERIOD = 60 * 1000; private static final int CLOCK_NEXT = 5 * 1000; private static final int CLOCK_INTERVAL = 1; private Alarm []_clockArray = new Alarm[CLOCK_PERIOD]; private AtomicLong _lastExtractTime = new AtomicLong(); private AtomicLong _nextAlarmTime = new AtomicLong(); private final ArrayList _currentAlarms = new ArrayList(); private long _lastTime; private Object _lock = new Object(); private ThreadPool _threadPool = ThreadPool.getThreadPool(); /** * Queue the alarm for wakeup. * * @param delta time in milliseconds to wake */ public boolean queueAt(Alarm alarm, long wakeTime) { boolean isEarliest = false; long prevNextAlarmTime; do { prevNextAlarmTime = _nextAlarmTime.get(); } while (wakeTime > 0 && wakeTime < prevNextAlarmTime && ! _nextAlarmTime.compareAndSet(prevNextAlarmTime, wakeTime)); if (wakeTime < prevNextAlarmTime) isEarliest = true; long oldWakeTime = alarm.getAndSetWakeTime(wakeTime); if (oldWakeTime == wakeTime) return false; if (oldWakeTime > 0) { if (! dequeueImpl(alarm)) { /* System.out.println("FAIL: " + oldWakeTime + " " + wakeTime + " " + alarm); */ } } if (wakeTime <= 0) return false; long lastExtractTime = _lastExtractTime.get(); if (wakeTime <= lastExtractTime) { queueCurrent(alarm); return true; } synchronized (_lock) { if (alarm.getBucket() >= 0) return false; int bucket = getBucket(wakeTime); alarm.setBucket(bucket); Alarm top = _clockArray[bucket]; alarm.setNext(top); _clockArray[bucket] = alarm; } lastExtractTime = _lastExtractTime.get(); long nextWakeTime = alarm.getWakeTime(); if (nextWakeTime <= lastExtractTime && nextWakeTime > 0) { if (dequeueImpl(alarm)) { queueCurrent(alarm); } } return isEarliest; } private void queueCurrent(Alarm alarm) { synchronized (_currentAlarms) { _currentAlarms.add(alarm); } } void dequeue(Alarm alarm) { // long oldWakeTime = alarm.getAndSetWakeTime(0); // long oldWakeTime = alarm.getAndSetWakeTime(0); alarm.setWakeTime(0); if (alarm.getBucket() >= 0) { dequeueImpl(alarm); } } private boolean dequeueImpl(Alarm alarm) { synchronized (_lock) { int bucket = alarm.getBucket(); Alarm next = alarm.getNext(); alarm.setBucket(-1); alarm.setNext(null); if (bucket < 0) return false; Alarm head = _clockArray[bucket]; if (head == null) return false; if (head == alarm) { _clockArray[bucket] = next; return true; } Alarm prev = head; Alarm ptr = prev.getNext(); while (ptr != null) { if (ptr == alarm) { prev.setNext(next); return true; } prev = ptr; ptr = ptr.getNext(); } } return false; } private Alarm extractNextAlarm(int bucket, long time, boolean isTest) { if (_clockArray[bucket] == null) return null; synchronized (_lock) { Alarm ptr = _clockArray[bucket]; Alarm prev = null; while (ptr != null) { Alarm next = ptr.getNext(); long wakeTime = ptr.getWakeTime(); if (wakeTime <= time) { ptr.setNext(null); ptr.setBucket(-1); if (prev != null) { prev.setNext(next); } else { _clockArray[bucket] = next; } return ptr; } prev = ptr; ptr = next; } } return null; } /** * Returns the next alarm ready to run */ public long extractAlarm(long now, boolean isTest) { long lastTime = _lastExtractTime.getAndSet(now); long nextTime = _nextAlarmTime.get(); if (now < nextTime) { return nextTime; } _nextAlarmTime.set(now + CLOCK_NEXT); int delta; delta = (int) (now - lastTime) / CLOCK_INTERVAL; delta = Math.min(delta, CLOCK_PERIOD); Alarm alarm; int bucket = getBucket(lastTime); for (int i = 0; i <= delta; i++) { // long time = lastTime + i; while ((alarm = extractNextAlarm(bucket, now, isTest)) != null) { dispatch(alarm, now, isTest); } bucket = (bucket + 1) % CLOCK_PERIOD; } while ((alarm = extractNextCurrentAlarm()) != null) { dispatch(alarm, now, isTest); } long next = updateNextAlarmTime(now); _lastTime = now; return next; } private Alarm extractNextCurrentAlarm() { if (_currentAlarms.size() == 0) return null; synchronized (_currentAlarms) { if (_currentAlarms.size() > 0) { Alarm alarm = _currentAlarms.remove(_currentAlarms.size() - 1); return alarm; } return null; } } private long updateNextAlarmTime(long now) { long nextTime = _nextAlarmTime.get(); long delta = Math.min(nextTime - now, CLOCK_PERIOD); synchronized (_lock) { for (int i = 0; i < delta; i++) { long time = now + i; if (nextTime < time) return nextTime; int bucket = getBucket(time); for (Alarm ptr = _clockArray[bucket]; ptr != null; ptr = ptr.getNext()) { // Alarm alarm = _clockArray[bucket]; long alarmTime = ptr.getWakeTime(); while (alarmTime < nextTime && alarmTime > 0) { _nextAlarmTime.compareAndSet(nextTime, alarmTime); nextTime = _nextAlarmTime.get(); } } } } return nextTime; } private void dispatch(Alarm alarm, long now, boolean isTest) { boolean isStressTest = false; if (isStressTest) now = CurrentTime.getExactTime(); else now = CurrentTime.getCurrentTime(); long wakeTime = alarm.getAndSetWakeTime(0); long delta = now - wakeTime; if (wakeTime == 0) { // #5854 } else 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"); } if (isTest) { try { alarm.run(); } catch (Throwable e) { e.printStackTrace(); } } else if (alarm.isPriority()) { _threadPool.schedulePriority(alarm); } else { _threadPool.schedule(alarm); } } private int getBucket(long time) { return (int) ((time + CLOCK_INTERVAL - 1) / CLOCK_INTERVAL % CLOCK_PERIOD); } /** * Returns the next alarm ready to run */ long getNextAlarmTime() { return _nextAlarmTime.get(); } // test void testClear() { _lastExtractTime.set(0); _nextAlarmTime.set(0); synchronized (_lock) { _currentAlarms.clear(); for (int i = CLOCK_PERIOD - 1; i >= 0; i--) { Alarm alarm = _clockArray[i]; _clockArray[i] = null; while (alarm != null) { Alarm next = alarm.getNext(); alarm.setNext(null); alarm.setBucket(-1); alarm.setWakeTime(alarm.getWakeTime(), 0); alarm = next; } } } } @Override public String toString() { return getClass().getSimpleName(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy