net.java.truevfs.ext.insight.stats.FsLogger.scala Maven / Gradle / Ivy
/*
* Copyright (C) 2005-2015 Schlichtherle IT Services.
* All rights reserved. Use is subject to license terms.
*/
package net.java.truevfs.ext.insight.stats
import java.util.concurrent.atomic._
import javax.annotation.concurrent._
import FsLogger._
import scala.collection._
private object FsLogger {
private[this] val defaultSizePropertyKey = classOf[FsLogger].getName + ".defaultSize"
private val defaultSize = Integer getInteger (defaultSizePropertyKey, 10)
private[this] val maxValues = Array(
9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999,
Integer.MAX_VALUE
)
private def length(x: Int) = {
assert(0 <= x)
var i = 0
while (x > maxValues{ val j = i; i += 1; j }) {
}
i
}
private def atomic[V](ref: AtomicReference[V])(next: V => V): V = {
while (true) {
val expect = ref.get
val update = next(expect)
if (ref.compareAndSet(expect, update)) {
return update
}
}
throw new AssertionError
}
/**
* Adds a hash value for the current thread to the given set and returns its size.
*/
private def logCurrentThread(set: mutable.Set[Int]): Int = {
set synchronized {
(set += System identityHashCode Thread.currentThread).size
}
}
}
/**
* A logger for [[net.java.truevfs.ext.insight.stats.FsStatistics]]
* All operations get logged at offset zero.
*
* @author Christian Schlichtherle
*/
@ThreadSafe
final class FsLogger(val size: Int) {
def this() = this(defaultSize)
private[this] val _stats = {
val s = new AtomicReferenceArray[FsStatistics](size)
for (i <- 0 until size) s.set(i, FsStatistics())
s
}
private[this] val _position = new AtomicReference[Int]
@volatile private[this] var _readThreads = mutable.Set.empty[Int]
@volatile private[this] var _writeThreads = mutable.Set.empty[Int]
@volatile private[this] var _syncThreads = mutable.Set.empty[Int]
private def position = _position.get
private def index(offset: Int) = {
if (offset < 0 || size <= offset) {
throw new IllegalArgumentException
}
var index = position - offset
if (index < 0) {
index += size
}
index
}
def format(offset: Int): String = {
val max = size - 1
if (offset < 0 || max < offset) throw new IllegalArgumentException
"%%0%dd".format(length(max)).format(offset)
}
def stats(offset: Int): FsStatistics = _stats.get(index(offset))
def current: FsStatistics = stats(0)
private def update(next: FsStatistics => FsStatistics): FsStatistics = {
while (true) {
val expect = current
val update = next(expect)
if (_stats compareAndSet (position, expect, update)) {
return update
}
}
throw new AssertionError
}
/**
* Logs a read operation with the given sample data and returns a new
* object to reflect the updated statistics.
* The sequence number of the returned object will be incremented and may
* eventually overflow to zero.
*
* @param nanos the execution time in nanoseconds.
* @param bytes the number of bytes read.
* @return A new object which reflects the updated statistics.
* @throws IllegalArgumentException if any parameter value is negative.
*/
def logRead(nanos: Long, bytes: Int): IoStatistics = {
val threads = logCurrentThread(_readThreads)
update(_ logRead (nanos, bytes, threads)).readStats
}
/**
* Logs a write operation with the given sample data and returns a new
* object to reflect the updated statistics.
* The sequence number of the returned object will be incremented and may
* eventually overflow to zero.
*
* @param nanos the execution time in nanoseconds.
* @param bytes the number of bytes written.
* @return A new object which reflects the updated statistics.
* @throws IllegalArgumentException if any parameter is negative.
*/
def logWrite(nanos: Long, bytes: Int): IoStatistics = {
val threads = logCurrentThread(_writeThreads)
update(_ logWrite (nanos, bytes, threads)).writeStats
}
/**
* Logs a sync operation with the given sample data and returns a new
* object to reflect the updated statistics.
* The sequence number of the returned object will be incremented and may
* eventually overflow to zero.
*
* @param nanos the execution time in nanoseconds.
* @return A new object which reflects the updated statistics.
* @throws IllegalArgumentException if any parameter value is negative.
*/
def logSync(nanos: Long): SyncStatistics = {
val threads = logCurrentThread(_syncThreads)
update(_ logSync (nanos, threads)).syncStats
}
def rotate(): Int = {
val n = next()
_stats.set(n, FsStatistics())
_readThreads = mutable.Set.empty
_writeThreads = mutable.Set.empty
_syncThreads = mutable.Set.empty
n
}
private def next() = {
atomic(_position) { expect =>
var update = expect + 1
if (size <= update) {
update -= size
}
update
}
}
}