diff --git a/src/main/java/de/tudbut/security/DataKeeper.java b/src/main/java/de/tudbut/security/DataKeeper.java index d44d9d1..4c77558 100644 --- a/src/main/java/de/tudbut/security/DataKeeper.java +++ b/src/main/java/de/tudbut/security/DataKeeper.java @@ -2,6 +2,7 @@ package de.tudbut.security; import de.tudbut.obj.DoubleTypedObject; import de.tudbut.tools.Lock; +import de.tudbut.tools.SimpleLock; import java.util.LinkedList; import java.util.Queue; @@ -19,7 +20,7 @@ public class DataKeeper { private Supplier dataInsertion; private final Strictness strictness; private final Lock lock = new Lock(true); - private final Queue>, Lock>> nextFunctionToRun = new LinkedList<>(); + private final Queue>, SimpleLock>> nextFunctionToRun = new LinkedList<>(); private final Thread keeper = new Thread(this::keep, "DataKeeper"); { keeper.start(); } static { initSecurity(); } @@ -53,10 +54,10 @@ public class DataKeeper { else return; } - Lock waitLock = new Lock(true); + SimpleLock waitLock = new SimpleLock(true); nextFunctionToRun.add(new DoubleTypedObject<>(accessor, waitLock)); lock.unlock(); - waitLock.waitHere(500); + waitLock.waitHere(); } public void forget() { @@ -64,7 +65,7 @@ public class DataKeeper { } public DataKeeper forgetIn(int ms) { - if(forget.timeLeft() > ms) + if(forget.timeLeft() < ms) return this; forget.lock(ms); return this; @@ -81,14 +82,16 @@ public class DataKeeper { lock.waitHere(); lock.lock(500); - DoubleTypedObject>, Lock> itm = nextFunctionToRun.poll(); + DoubleTypedObject>, SimpleLock> itm = nextFunctionToRun.poll(); if(itm == null) continue; Consumer> toRun = itm.o; - Lock lock = itm.t; + SimpleLock lock = itm.t; // second layer of protection, crashes this time. - if(!permissionManager.checkLambda(strictness, toRun)) + if(!permissionManager.checkLambda(strictness, toRun)) { + lock.lock(); // never return permissionManager.crash(strictness); + } toRun.accept(new Accessor<>(permissionManager, strictness, data)); lock.unlock(); diff --git a/src/main/java/de/tudbut/tools/Lock.java b/src/main/java/de/tudbut/tools/Lock.java index 5da080b..7567ea6 100644 --- a/src/main/java/de/tudbut/tools/Lock.java +++ b/src/main/java/de/tudbut/tools/Lock.java @@ -1,55 +1,17 @@ package de.tudbut.tools; -import java.util.Date; -import java.util.concurrent.atomic.AtomicInteger; - /** * Helper for synchronization and timing */ -public class Lock { - - private final Locker locker = new Locker(); - private boolean locked = false; - private int t = 0; - private long ts = 0; - private final AtomicInteger waiting = new AtomicInteger(); - private volatile boolean[] waiterLocker = null; - - /** - * Object to handle thread locking - */ - private static class Locker { - - /** - * Make the thread wait until {@link Locker#unlock()} is called - * @throws InterruptedException Inherited from {@link Object#wait} - */ - public synchronized void lockHere() throws InterruptedException { - wait(); - } - - /** - * Make the thread wait until {@link Locker#unlock()} is called or the timeout runs out - * @throws InterruptedException Inherited from {@link Object#wait} - * @param timeout Maximal wait time - */ - public synchronized void lockHere(long timeout) throws InterruptedException { - wait(timeout); - } - - /** - * Stop locking - */ - public synchronized void unlock() { - notifyAll(); - } - } - +public class Lock extends SimpleLock { + + private long startTime = 0, lockTime = 0; + /** * Creates a Lock without default state */ public Lock() { - + super(); } /** @@ -57,95 +19,76 @@ public class Lock { * @param locked Default state */ public Lock(boolean locked) { - this.locked = locked; + super(locked); + } + + public synchronized long getStartTime() { + return startTime; + } + + public synchronized long timeLocked() { + return startTime != 0 ? System.currentTimeMillis() - startTime : 0; } /** - * + * @return The time left (or MAX_VALUE) + */ + public synchronized long timeLeft() { + if(lockTime == 0) + return Long.MAX_VALUE; + return waitTime(); + } + + /** * @return The time left */ - public long timeLeft() { - updateLocked(); - return locked ? (ts + t) - new Date().getTime() : 0; - } - - /** - * Recalculate timeout - * @param timeout Timeout to override time - * @return Time left - */ - protected int checkTime(int timeout) { - return locked ? checkNegative(Math.min((int) (t - (new Date().getTime() - ts ) ), timeout <= 0 ? Integer.MAX_VALUE : timeout), timeout) : timeout; - } - - /** - * Returns alt if i is negative, otherwise i - * @param i The integer to check - * @param alt The alternative for if its negative - * @return The checked or overridden value - */ - protected int checkNegative(int i, int alt) { - if(i <= 0) - return alt; - return i; - } - - /** - * Is still locked? - */ - protected void updateLocked() { - if(new Date().getTime() - ts >= t && ts != 0) - locked = false; + public synchronized long waitTime() { + return Math.max(lockTime - timeLocked(), 0); } /** * Wait until unlocked, either by a timer or manually */ - public void waitHere() { - updateLocked(); - if(locked) { - try { - locker.lockHere(checkTime(0)); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - updateLocked(); + public synchronized void waitHere() { + super.waitHere(waitTime()); } /** * Wait until unlocked, either by a timer, manually, or when it waited for timeout * @param timeout Timeout */ - public void waitHere(int timeout) { - updateLocked(); - if(locked) { - try { - locker.lockHere(checkTime(timeout)); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); + public synchronized void waitHere(int timeout) { + if(timeout == 0 || !super.isLocked()) + return; + + if(lockTime == 0) { + super.waitHere(timeout); + } else { + long timeLeft = lockTime - (System.currentTimeMillis() - startTime); + if (timeLeft == 0) { + unlock(); + return; } + super.waitHere(Math.min(timeLeft, timeout)); } - updateLocked(); } /** * Unlock manually */ public synchronized void unlock() { - locker.unlock(); - locked = false; + super.unlock(); + startTime = 0; + lockTime = 0; } /** * Lock until manually unlocked */ public synchronized void lock() { - t = 0; - ts = 0; - locked = true; + startTime = System.currentTimeMillis(); + lockTime = 0; + super.lock(); } /** @@ -153,64 +96,24 @@ public class Lock { * @param time The time to lock for */ public synchronized void lock(int time) { - if(time < 0) - time = 0; - locked = true; - t = time; - ts = new Date().getTime(); + if(time == 0) + return; + + startTime = System.currentTimeMillis(); + lockTime = time; + super.lock(); } /** - * * @return If the lock is locked */ public synchronized boolean isLocked() { - updateLocked(); - return locked; + update(); + return super.isLocked(); } - - /** - * Synchronize multiple threads on this lock - * @param amount The amount of threads to synchronize - */ - public void synchronize(int amount) { - this.locked = true; - if(waiterLocker == null) - waiterLocker = new boolean[amount]; - int i = waiting.get(); - waiting.getAndIncrement(); - locker.unlock(); - while (amount > waiting.get()) { - try { - locker.lockHere(); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - locker.unlock(); - boolean b; - waiterLocker[i] = true; - b = true; - try { - while (b) { - b = false; - for (int j = 0 ; j < waiterLocker.length ; j++) { - if (!waiterLocker[j]) { - b = true; - break; - } - } - } - } catch (Exception ignored) { } - try { - Thread.sleep(1); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - waiting.getAndDecrement(); - waiterLocker = null; - this.locked = false; + + private synchronized void update() { + if(timeLeft() == 0) + unlock(); } } diff --git a/src/main/java/de/tudbut/tools/SimpleLock.java b/src/main/java/de/tudbut/tools/SimpleLock.java new file mode 100644 index 0000000..7f38417 --- /dev/null +++ b/src/main/java/de/tudbut/tools/SimpleLock.java @@ -0,0 +1,38 @@ +package de.tudbut.tools; + +public class SimpleLock { + private boolean locked = false; + + public SimpleLock() { } + + public SimpleLock(boolean locked) { + this.locked = locked; + } + + public synchronized void lock() { + locked = true; + } + + public void waitHere() { + waitHere(0); + } + + public synchronized void waitHere(long timeout) { + if(locked) { + try { + wait(timeout); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + public synchronized void unlock() { + locked = false; + notifyAll(); + } + + public synchronized boolean isLocked() { + return locked; + } +} diff --git a/src/test/java/LockTest.java b/src/test/java/LockTest.java new file mode 100644 index 0000000..9c8a1b6 --- /dev/null +++ b/src/test/java/LockTest.java @@ -0,0 +1,39 @@ +import de.tudbut.tools.Lock; + +public class LockTest { + public static void main(String[] args) throws InterruptedException { + System.out.println("Timer test"); + Lock lock = new Lock(); + for (int i = 0; i < 5; i++) { + long start = System.currentTimeMillis(); + lock.lock(500); + Thread.sleep(250); + System.out.println("Time locked/left after 250ms: " + lock.timeLocked() + "/" + lock.timeLeft()); + lock.waitHere(); + System.out.println("Took " + (System.currentTimeMillis() - start) + ". (Should be 500)"); + } + System.out.println("Timer test with shortcut"); + for (int i = 0; i < 5; i++) { + long start = System.currentTimeMillis(); + lock.lock(5000); + Thread.sleep(250); + System.out.println("Time locked/left after 250ms: " + lock.timeLocked() + "/" + lock.timeLeft()); + lock.waitHere(500); + System.out.println("Took " + (System.currentTimeMillis() - start) + ". (Should be 750)"); + } + System.out.println("Sync test"); + lock.lock(); + System.out.println("Thread 2 should be up first but start after 1. Lock has timeout: " + lock.timeLeft()); + new Thread(() -> { + System.out.println("Thread 2 up"); + lock.waitHere(); + System.out.println("Thread 2 started"); + }).start(); + Thread.sleep(10); + new Thread(() -> { + System.out.println("Thread 1 up"); + System.out.println("Thread 1 started"); + lock.unlock(); + }).start(); + } +}