diff src/fschmidt/util/locks/TimedLock.java @ 68:00520880ad02

add fschmidt source
author Franklin Schmidt <fschmidt@gmail.com>
date Sun, 05 Oct 2025 17:24:15 -0600
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/fschmidt/util/locks/TimedLock.java	Sun Oct 05 17:24:15 2025 -0600
@@ -0,0 +1,173 @@
+/*
+Copyright (c) 2008  Franklin Schmidt <fschmidt@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+package fschmidt.util.locks;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public final class TimedLock extends ProxyLock {
+	private static final Logger logger = LoggerFactory.getLogger(TimedLock.class);
+
+	private static long timeoutDefault = 10L*60L*1000L;  // 10 minutes
+	private static long waitTimeDefault = 60L*1000L;  // 1 minutes
+
+	public static long getTimeoutDefault() {
+		return timeoutDefault;
+	}
+
+	public static void setTimeoutDefault(long timeoutDefault) {
+		TimedLock.timeoutDefault = timeoutDefault;
+	}
+
+	public static long getWaitTimeDefault() {
+		return waitTimeDefault;
+	}
+
+	public static void setWaitTimeDefault(long waitTimeDefault) {
+		TimedLock.waitTimeDefault = waitTimeDefault;
+	}
+
+	private static Set<TimedLock> locks = Collections.synchronizedSet(new HashSet<TimedLock>());
+	private int count = 0;
+	private final Object sync = new Object();
+	private long whenLocked;
+	private long whenChecked;
+	private long timeout = timeoutDefault;
+	private long waitTime = waitTimeDefault;
+	private Thread thread = null;
+
+	public TimedLock(Lock lock) {
+		super(lock);
+	}
+
+	public long getTimeout() {
+		return timeout;
+	}
+
+	public void setTimeout(long timeout) {
+		this.timeout = timeout;
+	}
+
+	public long getWaitTime() {
+		return waitTime;
+	}
+
+	public void setWaitTime(long waitTime) {
+		this.waitTime = waitTime;
+	}
+
+	private void locked() {
+		synchronized(sync) {
+			if( count++ == 0 ) {
+				whenLocked = System.currentTimeMillis();
+				whenChecked = whenLocked;
+				thread = Thread.currentThread();
+				if( !locks.add(this) )
+					throw new RuntimeException();
+			}
+		}
+	}
+
+	public boolean tryLock() {
+		if( !super.tryLock() )
+			return false;
+		locked();
+		return true;
+	}
+
+	public boolean tryLock(long time,TimeUnit unit)
+		throws InterruptedException
+	{
+		long t = TimeUnit.MILLISECONDS.convert(time,unit);
+		while( t > waitTime ) {
+			if( super.tryLock(waitTime,TimeUnit.MILLISECONDS) ) {
+				locked();
+				return true;
+			}
+			t -= waitTime;
+			check();
+		}
+		if( super.tryLock(t,TimeUnit.MILLISECONDS) ) {
+			locked();
+			return true;
+		}
+		return false;
+	}
+
+	public void lockInterruptibly()
+		throws InterruptedException
+	{
+		while(true) {
+			if( super.tryLock(waitTime,TimeUnit.MILLISECONDS) ) {
+				locked();
+				return;
+			}
+			check();
+		}
+	}
+
+	public void lock() {
+		try {
+			lockInterruptibly();
+		} catch(InterruptedException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public void unlock() {
+		synchronized(sync) {
+			if( --count == 0 ) {
+				if( !locks.remove(this) )
+					throw new RuntimeException();
+				thread = null;
+			}
+		}
+		super.unlock();
+	}
+
+	private void check() {
+		long now = System.currentTimeMillis();
+		long tooOld = now - timeout;
+		TimedLock[] a = locks.toArray(new TimedLock[0]);
+		for( int i=0; i<a.length; i++ ) {
+			TimedLock tl = a[i];
+			if( tl.whenChecked < tooOld ) {
+				StringBuilder buf = new StringBuilder();
+				buf.append("lock "+tl+" timed out, locked for "+(System.currentTimeMillis()-tl.whenLocked)+" millis");
+				buf.append("\n"+tl.thread);
+				for( StackTraceElement ste : tl.thread.getStackTrace() ) {
+					buf.append("\n\t"+ste);
+				}
+				logger.error(buf.toString());
+				tl.whenChecked = now;
+			}
+		}
+	}
+
+}