mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2024-09-29 23:17:41 +02:00
IQueueRORA as interface, add an implementation using a Lock.
+ Use in AbstractLogNodeDispatcher.
This commit is contained in:
parent
73a62a1e13
commit
2de5f2fd3a
@ -17,6 +17,7 @@ package fr.neatmonster.nocheatplus.logging.details;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.ds.corw.IQueueRORA;
|
||||||
import fr.neatmonster.nocheatplus.utilities.ds.corw.QueueRORA;
|
import fr.neatmonster.nocheatplus.utilities.ds.corw.QueueRORA;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,19 +26,19 @@ import fr.neatmonster.nocheatplus.utilities.ds.corw.QueueRORA;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractLogNodeDispatcher implements LogNodeDispatcher { // TODO: Name
|
public abstract class AbstractLogNodeDispatcher implements LogNodeDispatcher { // TODO: Name
|
||||||
|
|
||||||
// TODO: Queues might need a drop policy with thresholds.
|
// TODO: Queues might need a drop policy with thresholds.
|
||||||
// TODO: Allow multiple tasks for logging, e.g. per file, also thinking of SQL logging. Could pool + round-robin.
|
// TODO: Allow multiple tasks for logging, e.g. per file, also thinking of SQL logging. Could pool + round-robin.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This queue has to be processed in a task within the primary thread with calling runLogsPrimary.
|
* This queue has to be processed in a task within the primary thread with calling runLogsPrimary.
|
||||||
*/
|
*/
|
||||||
protected final QueueRORA<LogRecord<?>> queuePrimary = new QueueRORA<LogRecord<?>>();
|
protected final IQueueRORA<LogRecord<?>> queuePrimary = new QueueRORA<LogRecord<?>>();
|
||||||
protected final QueueRORA<LogRecord<?>> queueAsynchronous = new QueueRORA<LogRecord<?>>();
|
protected final IQueueRORA<LogRecord<?>> queueAsynchronous = new QueueRORA<LogRecord<?>>();
|
||||||
|
|
||||||
/** Once a queue reaches this size, it will be reduced (loss of content). */
|
/** Once a queue reaches this size, it will be reduced (loss of content). */
|
||||||
protected int maxQueueSize = 5000;
|
protected int maxQueueSize = 5000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Task id, -1 means the asynchronous task is not running. Synchronize over
|
* Task id, -1 means the asynchronous task is not running. Synchronize over
|
||||||
* queueAsynchronous. Must be maintained.
|
* queueAsynchronous. Must be maintained.
|
||||||
@ -48,7 +49,7 @@ public abstract class AbstractLogNodeDispatcher implements LogNodeDispatcher { /
|
|||||||
* taskAsynchronousID, synchronized over queueAsynchronous.
|
* taskAsynchronousID, synchronized over queueAsynchronous.
|
||||||
*/
|
*/
|
||||||
protected final Runnable taskAsynchronous = new Runnable() {
|
protected final Runnable taskAsynchronous = new Runnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// TODO: A more sophisticated System to allow "wake up on burst"?
|
// TODO: A more sophisticated System to allow "wake up on burst"?
|
||||||
@ -86,9 +87,9 @@ public abstract class AbstractLogNodeDispatcher implements LogNodeDispatcher { /
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional init logger to log errors. Should log to the init stream, no queuing.
|
* Optional init logger to log errors. Should log to the init stream, no queuing.
|
||||||
*/
|
*/
|
||||||
@ -102,16 +103,16 @@ public abstract class AbstractLogNodeDispatcher implements LogNodeDispatcher { /
|
|||||||
scheduleLog(node, level, content);
|
scheduleLog(node, level, content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean runLogsPrimary() {
|
protected boolean runLogsPrimary() {
|
||||||
return runLogs(queuePrimary);
|
return runLogs(queuePrimary);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean runLogsAsynchronous() {
|
protected boolean runLogsAsynchronous() {
|
||||||
return runLogs(queueAsynchronous);
|
return runLogs(queueAsynchronous);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean runLogs(final QueueRORA<LogRecord<?>> queue) {
|
private boolean runLogs(final IQueueRORA<LogRecord<?>> queue) {
|
||||||
// TODO: Consider allowYield + msYield, calling yield after 5 ms if async.
|
// TODO: Consider allowYield + msYield, calling yield after 5 ms if async.
|
||||||
final List<LogRecord<?>> records = queue.removeAll();
|
final List<LogRecord<?>> records = queue.removeAll();
|
||||||
if (records.isEmpty()) {
|
if (records.isEmpty()) {
|
||||||
@ -122,7 +123,7 @@ public abstract class AbstractLogNodeDispatcher implements LogNodeDispatcher { /
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flush(long ms) {
|
public void flush(long ms) {
|
||||||
if (!isPrimaryThread()) {
|
if (!isPrimaryThread()) {
|
||||||
@ -130,7 +131,7 @@ public abstract class AbstractLogNodeDispatcher implements LogNodeDispatcher { /
|
|||||||
throw new IllegalStateException("Must only be called from within the primary thread.");
|
throw new IllegalStateException("Must only be called from within the primary thread.");
|
||||||
}
|
}
|
||||||
// TODO: Note that all streams should be emptied here, except the fallback logger.
|
// TODO: Note that all streams should be emptied here, except the fallback logger.
|
||||||
|
|
||||||
// Cancel task.
|
// Cancel task.
|
||||||
synchronized (queueAsynchronous) {
|
synchronized (queueAsynchronous) {
|
||||||
if (taskAsynchronousID != -1) {
|
if (taskAsynchronousID != -1) {
|
||||||
@ -150,12 +151,12 @@ public abstract class AbstractLogNodeDispatcher implements LogNodeDispatcher { /
|
|||||||
// Ignore.
|
// Ignore.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the rest (from here logging should be done via the appropriate direct-only stream).
|
// Log the rest (from here logging should be done via the appropriate direct-only stream).
|
||||||
runLogsPrimary();
|
runLogsPrimary();
|
||||||
runLogsAsynchronous();
|
runLogsAsynchronous();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <C> boolean isWithinContext(LogNode<C> node) {
|
protected <C> boolean isWithinContext(LogNode<C> node) {
|
||||||
switch (node.options.callContext) {
|
switch (node.options.callContext) {
|
||||||
case PRIMARY_THREAD_DIRECT:
|
case PRIMARY_THREAD_DIRECT:
|
||||||
@ -174,7 +175,7 @@ public abstract class AbstractLogNodeDispatcher implements LogNodeDispatcher { /
|
|||||||
return false; // Force scheduling.
|
return false; // Force scheduling.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <C> void scheduleLog(LogNode<C> node, Level level, C content) {
|
protected <C> void scheduleLog(LogNode<C> node, Level level, C content) {
|
||||||
final LogRecord<C> record = new LogRecord<C>(node, level, content); // TODO: parameters.
|
final LogRecord<C> record = new LogRecord<C>(node, level, content); // TODO: parameters.
|
||||||
switch (node.options.callContext) {
|
switch (node.options.callContext) {
|
||||||
@ -201,12 +202,12 @@ public abstract class AbstractLogNodeDispatcher implements LogNodeDispatcher { /
|
|||||||
throw new IllegalArgumentException("Bad CallContext: " + node.options.callContext);
|
throw new IllegalArgumentException("Bad CallContext: " + node.options.callContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hard reduce the queue (heavy locking!).
|
* Hard reduce the queue (heavy locking!).
|
||||||
* @param queue
|
* @param queue
|
||||||
*/
|
*/
|
||||||
private void reduceQueue(final QueueRORA<LogRecord<?>> queue) {
|
private void reduceQueue(final IQueueRORA<LogRecord<?>> queue) {
|
||||||
// TODO: Different dropping strategies (drop first, last, alternate).
|
// TODO: Different dropping strategies (drop first, last, alternate).
|
||||||
final int dropped;
|
final int dropped;
|
||||||
synchronized (queue) {
|
synchronized (queue) {
|
||||||
@ -220,17 +221,17 @@ public abstract class AbstractLogNodeDispatcher implements LogNodeDispatcher { /
|
|||||||
}
|
}
|
||||||
logINIT(Level.WARNING, "Dropped " + dropped + " log entries from the " + (queue == queueAsynchronous ? "asynchronous" : "primary thread") + " queue.");
|
logINIT(Level.WARNING, "Dropped " + dropped + " log entries from the " + (queue == queueAsynchronous ? "asynchronous" : "primary thread") + " queue.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMaxQueueSize(int maxQueueSize) {
|
public void setMaxQueueSize(int maxQueueSize) {
|
||||||
this.maxQueueSize = maxQueueSize;
|
this.maxQueueSize = maxQueueSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setInitLogger(ContentLogger<String> logger) {
|
public void setInitLogger(ContentLogger<String> logger) {
|
||||||
this.initLogger = logger;
|
this.initLogger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void logINIT(final Level level, final String message) {
|
protected void logINIT(final Level level, final String message) {
|
||||||
if (initLogger != null) {
|
if (initLogger != null) {
|
||||||
initLogger.log(level, message);
|
initLogger.log(level, message);
|
||||||
@ -238,9 +239,9 @@ public abstract class AbstractLogNodeDispatcher implements LogNodeDispatcher { /
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected abstract boolean isPrimaryThread();
|
protected abstract boolean isPrimaryThread();
|
||||||
|
|
||||||
protected abstract void scheduleAsynchronous();
|
protected abstract void scheduleAsynchronous();
|
||||||
|
|
||||||
protected abstract void cancelTask(int taskId);
|
protected abstract void cancelTask(int taskId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package fr.neatmonster.nocheatplus.utilities.ds.corw;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread-safe queue - a replace-on-read-all queue-thing, supposedly exchanging
|
||||||
|
* the internally stored list by a new empty one under lock, for a very small
|
||||||
|
* locking time, so it is not really a typical copy-on-read. All methods use
|
||||||
|
* locking, implementation-specific.
|
||||||
|
*
|
||||||
|
* @author asofold
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface IQueueRORA<E> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add to list.
|
||||||
|
*
|
||||||
|
* @param element
|
||||||
|
* @return Size of queue after adding.
|
||||||
|
*/
|
||||||
|
public int add(final E element);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return An ordinary List containing all elements. This should be the
|
||||||
|
* previously internally stored list, to keep locking time minimal.
|
||||||
|
*/
|
||||||
|
public List<E> removeAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove oldest entries until maxSize is reached.
|
||||||
|
*
|
||||||
|
* @param maxSize
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int reduce(final int maxSize);
|
||||||
|
|
||||||
|
|
||||||
|
public void clear();
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isEmpty();
|
||||||
|
|
||||||
|
|
||||||
|
public int size();
|
||||||
|
|
||||||
|
}
|
@ -19,23 +19,18 @@ import java.util.List;
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A replace-on-read-all queue-thing, exchanging the internal list under lock by
|
* IQueueRORA implementation using the synchronized keyword for locking, with a
|
||||||
* a new empty one, for a small locking time, so it is not really a typical
|
* LinkedList inside.
|
||||||
* copy-on-read. All methods use locking, the QueueRORA instance is used for
|
|
||||||
* locking.
|
|
||||||
*
|
*
|
||||||
* @author dev1mc
|
* @author asofold
|
||||||
*
|
*
|
||||||
|
* @param <E>
|
||||||
*/
|
*/
|
||||||
public class QueueRORA<E> {
|
public class QueueRORA<E> implements IQueueRORA<E> {
|
||||||
|
|
||||||
private LinkedList<E> elements = new LinkedList<E>();
|
private LinkedList<E> elements = new LinkedList<E>();
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Add to list (synchronized).
|
|
||||||
* @param element
|
|
||||||
* @return Size of queue after adding.
|
|
||||||
*/
|
|
||||||
public int add(final E element) {
|
public int add(final E element) {
|
||||||
final int size;
|
final int size;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
@ -44,11 +39,8 @@ public class QueueRORA<E> {
|
|||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
*
|
|
||||||
* @return An ordinary (linked) List containing all elements.
|
|
||||||
*/
|
|
||||||
public List<E> removeAll() {
|
public List<E> removeAll() {
|
||||||
final List<E> result;
|
final List<E> result;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
@ -57,12 +49,8 @@ public class QueueRORA<E> {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Remove oldest entries until maxSize is reached.
|
|
||||||
* @param maxSize
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public int reduce(final int maxSize) {
|
public int reduce(final int maxSize) {
|
||||||
int dropped = 0;
|
int dropped = 0;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
@ -77,15 +65,13 @@ public class QueueRORA<E> {
|
|||||||
}
|
}
|
||||||
return dropped;
|
return dropped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
removeAll();
|
removeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
final boolean isEmpty;
|
final boolean isEmpty;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
@ -93,11 +79,8 @@ public class QueueRORA<E> {
|
|||||||
}
|
}
|
||||||
return isEmpty;
|
return isEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public int size() {
|
public int size() {
|
||||||
final int size;
|
final int size;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
@ -105,5 +88,5 @@ public class QueueRORA<E> {
|
|||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
package fr.neatmonster.nocheatplus.utilities.ds.corw;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IQueueRORA implementation using an external Lock or a ReentrantLock for
|
||||||
|
* locking, with a LinkedList inside.
|
||||||
|
*
|
||||||
|
* @author asofold
|
||||||
|
*
|
||||||
|
* @param <E>
|
||||||
|
*/
|
||||||
|
public class QueueRORAWithLock<E> implements IQueueRORA<E> {
|
||||||
|
|
||||||
|
private final Lock lock;
|
||||||
|
private LinkedList<E> elements = new LinkedList<E>();
|
||||||
|
|
||||||
|
public QueueRORAWithLock() {
|
||||||
|
this(new ReentrantLock());
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueueRORAWithLock(Lock lock) {
|
||||||
|
this.lock = lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int add(final E element) {
|
||||||
|
lock.lock();
|
||||||
|
elements.add(element);
|
||||||
|
final int size = elements.size();
|
||||||
|
lock.unlock();
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<E> removeAll() {
|
||||||
|
lock.lock();
|
||||||
|
final List<E> result = elements;
|
||||||
|
elements = new LinkedList<E>();
|
||||||
|
lock.unlock();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int reduce(final int maxSize) {
|
||||||
|
int dropped = 0;
|
||||||
|
lock.lock();
|
||||||
|
final int size = elements.size();
|
||||||
|
if (size <= maxSize) {
|
||||||
|
return dropped;
|
||||||
|
}
|
||||||
|
while (dropped < size - maxSize) {
|
||||||
|
elements.removeFirst();
|
||||||
|
dropped ++;
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
return dropped;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
lock.lock();
|
||||||
|
final boolean isEmpty = elements.isEmpty();
|
||||||
|
lock.unlock();
|
||||||
|
return isEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
// TODO: Could maintain an int, simply.
|
||||||
|
lock.lock();
|
||||||
|
final int size = elements.size();
|
||||||
|
lock.unlock();
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user