net.sf.eBus.timer.EScheduledExecutorBlocking Maven / Gradle / Ivy
The newest version!
//
// Copyright 2024 Charles W. Rapp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package net.sf.eBus.timer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.Nullable;
import net.sf.eBus.config.EConfigure.ScheduledExecutor;
/**
* Uses a lock and condition to wait on timer expiration and to
* detect when a new timer is added or removed which impacts when
* the next timer expiration occurs. This timer has the greatest
* latency with respect to detecting an expiration and posting
* the associated task to the {@code EObject}'s event queue. The
* reason is that acquiring a lock and waiting on a condition
* will likely result in the executor thread being removed from
* its core. That means when the expiration occurs, the thread
* must wait to be placed back on a core before it can deliver
* the timer task to the eBus object queue.
*
* @see EScheduledExecutorSpinning
* @see EScheduledExecutorSpinSleep
* @see EScheduledExecutorSpinYield
*
* @author Charles W. Rapp
*/
/* package */ final class EScheduledExecutorBlocking
extends EScheduledExecutor
{
//---------------------------------------------------------------
// Member data.
//
//-----------------------------------------------------------
// Locals.
//
/**
* Acquire this lock prior when either updating timers set,
* retrieving next scheduled timer, or waiting for timer to
* expire.
*/
private final Lock mUpdateLock;
/**
* Signal scheduled executor thread that a new,
* next-to-expire timer was added to timers set.
*/
private final Condition mUpdateCondition;
//---------------------------------------------------------------
// Member methods.
//
//-----------------------------------------------------------
// Constructors.
//
/**
* Creates a new blocking scheduled executor based on builder
* settings.
* @param config contains executor configuration.
* @param startupSignal decrement signal when scheduled
* executor thread is up and running.
* @param shutdownSignal decrement signal when scheduled
* executor thread is shut down.
*/
@SuppressWarnings ({"java:S1172"})
/* package */ EScheduledExecutorBlocking(final ScheduledExecutor config,
final CountDownLatch startupSignal,
final CountDownLatch shutdownSignal)
{
super (config, startupSignal, shutdownSignal);
mUpdateLock = new ReentrantLock();
mUpdateCondition = mUpdateLock.newCondition();
} // end of EScheduledExecutorBlocking(...)
//
// end of Constructors.
//-----------------------------------------------------------
//-----------------------------------------------------------
// Abstract Method Implementations.
//
@Override
protected void addTimer(final ETimerImpl timer)
{
final ETimerImpl nextTimer = nextTimer();
mUpdateLock.lock();
try
{
mTimers.add(timer);
// Is this the new next-to-expire timer?
// It is if 1) there was no timer scheduled or
// 2) the new timer expires prior to the current
// next-to-expire timer.
if (nextTimer == null ||
timer.expiration() < nextTimer.expiration())
{
// Yes. Inform waitForExpiration of this fact.
mUpdateCondition.signal();
}
}
finally
{
mUpdateLock.unlock();
}
} // end of addTimer(ETimerImpl)
@Override
protected void removeTimer(final ETimerImpl timer)
{
final ETimerImpl nextTimer = nextTimer();
mUpdateLock.lock();
try
{
mTimers.remove(timer);
// Was the next timer just removed?
if (timer == nextTimer)
{
// Yes. Inform waitForExpiration of this fact.
mUpdateCondition.signal();
}
}
finally
{
mUpdateLock.unlock();
}
} // end of removeTimer(ETimerImpl)
@Override
protected @Nullable ETimerImpl pollTimer()
{
ETimerImpl retval = null;
mUpdateLock.lock();
try
{
// Wait for a new scheduled timer to appear or for
// scheduled executor to be shut down.
while (mRunFlag && (retval = nextTimer()) == null)
{
try
{
mUpdateCondition.await();
}
catch (InterruptedException interrupt)
{}
}
}
finally
{
mUpdateLock.unlock();
}
return (retval);
} // end of pollTimer()
// This lock/condition is inside the EScheduledExector.run()
// while loop.
@SuppressWarnings ({"java:S2274"})
@Override
protected boolean waitForExpiration(final ETimerImpl timer)
{
final long delay = (timer.delay()).toNanos();
boolean retcode = false;
mUpdateLock.lock();
try
{
// Timer expired if awaitNanos returns value <= zero.
retcode = (mUpdateCondition.awaitNanos(delay) <= 0L);
}
catch (InterruptedException interrupt)
{
// Ignore and return false.
}
finally
{
mUpdateLock.unlock();
}
return (retcode);
} // end of waitForExpiration(IETimer)
//
// end of Abstract Method Implementations.
//-----------------------------------------------------------
} // end of class EScheduledExecutorBlocking