scala.actors.threadpool.helpers.Utils Maven / Gradle / Ivy
Show all versions of scala-actors Show documentation
/*
* Written by Dawid Kurzyniec, based on code written by Doug Lea with assistance
* from members of JCP JSR-166 Expert Group. Released to the public domain,
* as explained at http://creativecommons.org/licenses/publicdomain.
*
* Thanks to Craig Mattocks for suggesting to use sun.misc.Perf
.
*/
package scala.actors.threadpool.helpers;
//import edu.emory.mathcs.backport.java.util.*;
import scala.actors.threadpool.*;
import scala.actors.threadpool.locks.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.lang.reflect.Array;
import java.util.Iterator;
import java.util.Collection;
/**
*
* This class groups together the functionality of java.util.concurrent that
* cannot be fully and reliably implemented in backport, but for which some
* form of emulation is possible.
*
* Currently, this class contains methods related to nanosecond-precision
* timing, particularly via the {@link #nanoTime} method. To measure time
* accurately, this method by default uses java.sun.Perf
on
* JDK1.4.2 and it falls back to System.currentTimeMillis
* on earlier JDKs.
*
* @author Dawid Kurzyniec
* @version 1.0
*/
public final class Utils {
private final static NanoTimer nanoTimer;
private final static String providerProp =
"edu.emory.mathcs.backport.java.util.concurrent.NanoTimerProvider";
static {
NanoTimer timer = null;
try {
String nanoTimerClassName =
AccessController.doPrivileged(new PrivilegedAction() {
public String run() {
return System.getProperty(providerProp);
}
});
if (nanoTimerClassName != null) {
Class cls = Class.forName(nanoTimerClassName);
timer = (NanoTimer) cls.newInstance();
}
}
catch (Exception e) {
System.err.println("WARNING: unable to load the system-property-defined " +
"nanotime provider; switching to the default");
e.printStackTrace();
}
if (timer == null) {
try {
timer = new SunPerfProvider();
}
catch (Throwable e) {}
}
if (timer == null) {
timer = new MillisProvider();
}
nanoTimer = timer;
}
private Utils() {}
/**
* Returns the current value of the most precise available system timer,
* in nanoseconds. This method can only be used to measure elapsed time and
* is not related to any other notion of system or wall-clock time. The
* value returned represents nanoseconds since some fixed but arbitrary
* time (perhaps in the future, so values may be negative). This method
* provides nanosecond precision, but not necessarily nanosecond accuracy.
* No guarantees are made about how frequently values change. Differences
* in successive calls that span greater than approximately 292 years
* (2^63 nanoseconds) will not accurately compute elapsed time due to
* numerical overflow.
*
* Implementation note:By default, this method uses
* sun.misc.Perf
on Java 1.4.2, and falls back to
* System.currentTimeMillis() emulation on earlier JDKs. Custom
* timer can be provided via the system property
* edu.emory.mathcs.backport.java.util.concurrent.NanoTimerProvider
.
* The value of the property should name a class implementing
* {@link NanoTimer} interface.
*
* Note: on JDK 1.4.2, sun.misc.Perf
timer seems to have
* resolution of the order of 1 microsecond, measured on Linux.
*
* @return The current value of the system timer, in nanoseconds.
*/
public static long nanoTime() {
return nanoTimer.nanoTime();
}
/**
* Causes the current thread to wait until it is signalled or interrupted,
* or the specified waiting time elapses. This method originally appears
* in the {@link Condition} interface, but it was moved to here since it
* can only be emulated, with very little accuracy guarantees: the
* efficient implementation requires accurate nanosecond timer and native
* support for nanosecond-precision wait queues, which are not usually
* present in JVMs prior to 1.5. Loss of precision may cause total waiting
* times to be systematically shorter than specified when re-waits occur.
*
*
The lock associated with this condition is atomically
* released and the current thread becomes disabled for thread scheduling
* purposes and lies dormant until one of five things happens:
*
* - Some other thread invokes the {@link
* edu.emory.mathcs.backport.java.util.concurrent.locks.Condition#signal}
* method for this
* Condition and the current thread happens to be chosen as the
* thread to be awakened; or
*
- Some other thread invokes the {@link
* edu.emory.mathcs.backport.java.util.concurrent.locks.Condition#signalAll}
* method for this
* Condition; or
*
- Some other thread {@link Thread#interrupt interrupts} the current
* thread, and interruption of thread suspension is supported; or
*
- The specified waiting time elapses; or
*
- A "spurious wakeup" occurs.
*
*
* In all cases, before this method can return the current thread must
* re-acquire the lock associated with this condition. When the
* thread returns it is guaranteed to hold this lock.
*
*
If the current thread:
*
* - has its interrupted status set on entry to this method; or
*
- is {@link Thread#interrupt interrupted} while waiting
* and interruption of thread suspension is supported,
*
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared. It is not specified, in the first
* case, whether or not the test for interruption occurs before the lock
* is released.
*
* The method returns an estimate of the number of nanoseconds
* remaining to wait given the supplied nanosTimeout
* value upon return, or a value less than or equal to zero if it
* timed out. Accuracy of this estimate is directly dependent on the
* accuracy of {@link #nanoTime}. This value can be used to determine
* whether and how long to re-wait in cases where the wait returns but an
* awaited condition still does not hold. Typical uses of this method take
* the following form:
*
*
* synchronized boolean aMethod(long timeout, TimeUnit unit) {
* long nanosTimeout = unit.toNanos(timeout);
* while (!conditionBeingWaitedFor) {
* if (nanosTimeout > 0)
* nanosTimeout = theCondition.awaitNanos(nanosTimeout);
* else
* return false;
* }
* // ...
* }
*
*
* Implementation Considerations
*
The current thread is assumed to hold the lock associated with this
* Condition when this method is called.
* It is up to the implementation to determine if this is
* the case and if not, how to respond. Typically, an exception will be
* thrown (such as {@link IllegalMonitorStateException}) and the
* implementation must document that fact.
*
*
A condition implementation can favor responding to an interrupt over
* normal method return in response to a signal, or over indicating the
* elapse of the specified waiting time. In either case the implementation
* must ensure that the signal is redirected to another waiting thread, if
* there is one.
*
* @param cond the condition to wait for
* @param nanosTimeout the maximum time to wait, in nanoseconds
* @return A value less than or equal to zero if the wait has
* timed out; otherwise an estimate, that
* is strictly less than the nanosTimeout argument,
* of the time still remaining when this method returned.
*
* @throws InterruptedException if the current thread is interrupted (and
* interruption of thread suspension is supported).
*/
public static long awaitNanos(Condition cond, long nanosTimeout)
throws InterruptedException
{
if (nanosTimeout <= 0) return nanosTimeout;
long now = nanoTime();
cond.await(nanosTimeout, TimeUnit.NANOSECONDS);
return nanosTimeout - (nanoTime() - now);
}
private static final class SunPerfProvider implements NanoTimer {
final Perf perf;
final long multiplier, divisor;
SunPerfProvider() {
perf =
AccessController.doPrivileged(new PrivilegedAction() {
public Perf run() {
return Perf.getPerf();
}
});
// trying to avoid BOTH overflow and rounding errors
long numerator = 1000000000;
long denominator = perf.highResFrequency();
long gcd = gcd(numerator, denominator);
this.multiplier = numerator / gcd;
this.divisor = denominator / gcd;
}
public long nanoTime() {
long ctr = perf.highResCounter();
// anything less sophisticated suffers either from rounding errors
// (FP arithmetics, backport v1.0) or overflow, when gcd is small
// (a bug in backport v1.0_01 reported by Ramesh Nethi)
return ((ctr / divisor) * multiplier) +
(ctr % divisor) * multiplier / divisor;
// even the above can theoretically cause problems if your JVM is
// running for sufficiently long time, but "sufficiently" means 292
// years (worst case), or 30,000 years (common case).
// Details: when the ticks ctr overflows, there is no way to avoid
// discontinuity in computed nanos, even in infinite arithmetics,
// unless we count number of overflows that the ctr went through
// since the JVM started. This follows from the fact that
// (2^64*multiplier/divisor) mod (2^64) > 0 in general case.
// Theoretically we could find out the number of overflows by
// checking System.currentTimeMillis(), but this is unreliable
// since the system time can unpredictably change during the JVM
// lifetime.
// The time to overflow is 2^63 / ticks frequency. With current
// ticks frequencies of several MHz, it gives about 30,000 years
// before the problem happens. If ticks frequency reaches 1 GHz, the
// time to overflow is 292 years. It is unlikely that the frequency
// ever exceeds 1 GHz. We could double the time to overflow
// (to 2^64 / frequency) by using unsigned arithmetics, e.g. by
// adding the following correction whenever the ticks is negative:
// -2*((Long.MIN_VALUE / divisor) * multiplier +
// (Long.MIN_VALUE % divisor) * multiplier / divisor)
// But, with the worst case of as much as 292 years, it does not
// seem justified.
}
}
private static final class MillisProvider implements NanoTimer {
MillisProvider() {}
public long nanoTime() {
return System.currentTimeMillis() * 1000000;
}
}
private static long gcd(long a, long b) {
long r;
while (b>0) { r = a % b; a = b; b = r; }
return a;
}
public static Object[] collectionToArray(Collection c) {
// guess the array size; expect to possibly be different
int len = c.size();
Object[] arr = new Object[len];
Iterator itr = c.iterator();
int idx = 0;
while (true) {
while (idx < len && itr.hasNext()) {
arr[idx++] = itr.next();
}
if (!itr.hasNext()) {
if (idx == len) return arr;
// otherwise have to trim
return Arrays.copyOf(arr, idx, Object[].class);
}
// otherwise, have to grow
int newcap = ((arr.length/2)+1)*3;
if (newcap < arr.length) {
// overflow
if (arr.length < Integer.MAX_VALUE) {
newcap = Integer.MAX_VALUE;
}
else {
throw new OutOfMemoryError("required array size too large");
}
}
arr = Arrays.copyOf(arr, newcap, Object[].class);
len = newcap;
}
}
public static Object[] collectionToArray(Collection c, Object[] a) {
Class aType = a.getClass();
// guess the array size; expect to possibly be different
int len = c.size();
Object[] arr = (a.length >= len ? a :
(Object[])Array.newInstance(aType.getComponentType(), len));
Iterator itr = c.iterator();
int idx = 0;
while (true) {
while (idx < len && itr.hasNext()) {
arr[idx++] = itr.next();
}
if (!itr.hasNext()) {
if (idx == len) return arr;
if (arr == a) {
// orig array -> null terminate
a[idx] = null;
return a;
}
else {
// have to trim
return Arrays.copyOf(arr, idx, aType);
}
}
// otherwise, have to grow
int newcap = ((arr.length/2)+1)*3;
if (newcap < arr.length) {
// overflow
if (arr.length < Integer.MAX_VALUE) {
newcap = Integer.MAX_VALUE;
}
else {
throw new OutOfMemoryError("required array size too large");
}
}
arr = Arrays.copyOf(arr, newcap, aType);
len = newcap;
}
}
}