From e4ae7a319512dcea0e6ad996a31460c2ce1d1452 Mon Sep 17 00:00:00 2001 From: Ao Li <5557706+aoli-al@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:35:01 -0500 Subject: [PATCH] Support timed tryLock. (#54) --- .../org/pastalab/fray/core/RunContext.kt | 28 ++++++++++++++----- .../org/pastalab/fray/core/RuntimeDelegate.kt | 13 ++------- .../core/concurrency/locks/LockContext.kt | 2 ++ .../core/concurrency/locks/LockManager.kt | 4 +++ .../concurrency/locks/ReentrantLockContext.kt | 4 +++ .../locks/ReentrantReadWriteLockContext.kt | 8 ++++++ .../concurrency/operations/LockBlocking.kt | 2 +- .../success/lock/ReentrantLockTryLock.java | 21 ++++++++++++++ .../org/pastalab/fray/test/FrayTestCase.java | 3 +- .../org/pastalab/fray/runtime/Delegate.java | 4 --- .../org/pastalab/fray/runtime/Runtime.java | 4 --- 11 files changed, 65 insertions(+), 28 deletions(-) create mode 100644 integration-test/src/main/java/org/pastalab/fray/test/success/lock/ReentrantLockTryLock.java diff --git a/core/src/main/kotlin/org/pastalab/fray/core/RunContext.kt b/core/src/main/kotlin/org/pastalab/fray/core/RunContext.kt index cd1fb89d..facca964 100644 --- a/core/src/main/kotlin/org/pastalab/fray/core/RunContext.kt +++ b/core/src/main/kotlin/org/pastalab/fray/core/RunContext.kt @@ -596,11 +596,17 @@ class RunContext(val config: Configuration) { objectNotifyAllImpl(o, lockManager.lockFromCondition(o)) } - fun lockTryLock(lock: Any, canInterrupt: Boolean) { - lockImpl(lock, false, false, canInterrupt) + fun lockTryLock(lock: Any, canInterrupt: Boolean, timed: Boolean) { + lockImpl(lock, false, false, canInterrupt, timed) } - fun lockImpl(lock: Any, isMonitorLock: Boolean, shouldBlock: Boolean, canInterrupt: Boolean) { + fun lockImpl( + lock: Any, + isMonitorLock: Boolean, + shouldBlock: Boolean, + canInterrupt: Boolean, + timed: Boolean + ) { val t = Thread.currentThread().id val objId = System.identityHashCode(lock) val context = registeredThreads[t]!! @@ -612,6 +618,8 @@ class RunContext(val config: Configuration) { context.checkInterrupt() } + val blockingWait = shouldBlock || timed + /** * We need a while loop here because even a thread unlock this thread and makes this thread * Enabled. It is still possible for a third thread to lock it again. t1 = { @@ -629,9 +637,9 @@ class RunContext(val config: Configuration) { // synchronized(lock) { // lock.unlock(); // } - while (!lockManager.lock(lock, context, shouldBlock, false, canInterrupt) && shouldBlock) { + while (!lockManager.lock(lock, context, blockingWait, false, canInterrupt) && blockingWait) { context.state = ThreadState.Paused - context.pendingOperation = LockBlocking(lock) + context.pendingOperation = LockBlocking(lock, timed) // We want to block current thread because we do // not want to rely on ReentrantLock. This allows // us to pick which Thread to run next if multiple @@ -640,15 +648,21 @@ class RunContext(val config: Configuration) { if (canInterrupt) { context.checkInterrupt() } + val pendingOperation = context.pendingOperation + assert(pendingOperation is ThreadResumeOperation) + if (!(pendingOperation as ThreadResumeOperation).noTimeout) { + lockManager.tryLockUnblocked(lock, t) + break + } } } fun monitorEnter(lock: Any) { - lockImpl(lock, true, true, false) + lockImpl(lock, true, true, false, false) } fun lockLock(lock: Any, canInterrupt: Boolean) { - lockImpl(lock, false, true, canInterrupt) + lockImpl(lock, false, true, canInterrupt, false) } fun reentrantReadWriteLockInit(readLock: ReadLock, writeLock: WriteLock) { diff --git a/core/src/main/kotlin/org/pastalab/fray/core/RuntimeDelegate.kt b/core/src/main/kotlin/org/pastalab/fray/core/RuntimeDelegate.kt index f2b44e28..b2fb0e90 100644 --- a/core/src/main/kotlin/org/pastalab/fray/core/RuntimeDelegate.kt +++ b/core/src/main/kotlin/org/pastalab/fray/core/RuntimeDelegate.kt @@ -151,7 +151,7 @@ class RuntimeDelegate(val context: RunContext) : org.pastalab.fray.runtime.Deleg return } try { - context.lockTryLock(l, false) + context.lockTryLock(l, false, false) } finally { entered.set(false) onSkipMethod("Lock.tryLock") @@ -168,7 +168,7 @@ class RuntimeDelegate(val context: RunContext) : org.pastalab.fray.runtime.Deleg return timeout } try { - context.lockTryLock(l, true) + context.lockTryLock(l, true, true) } finally { entered.set(false) onSkipMethod("Lock.tryLock") @@ -803,15 +803,6 @@ class RuntimeDelegate(val context: RunContext) : org.pastalab.fray.runtime.Deleg return isInterrupted } - override fun onLockTryLockTimeout(l: Lock, timeout: Long, unit: TimeUnit): Boolean { - if (context.config.executionInfo.timedOpAsYield) { - onYield() - return false - } else { - return l.tryLock() - } - } - override fun onNanoTime(): Long { return context.nanoTime() } diff --git a/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/LockContext.kt b/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/LockContext.kt index 9699d20a..672b52e7 100644 --- a/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/LockContext.kt +++ b/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/LockContext.kt @@ -26,4 +26,6 @@ interface LockContext : Interruptible { fun isEmpty(): Boolean fun isLockHolder(lock: Any, tid: Long): Boolean + + fun tryLockUnblocked(lock: Any, tid: Long) } diff --git a/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/LockManager.kt b/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/LockManager.kt index 1245fae4..c4af3bd5 100644 --- a/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/LockManager.kt +++ b/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/LockManager.kt @@ -70,6 +70,10 @@ class LockManager { threadWaitsFor[t.id] = id } + fun tryLockUnblocked(lock: Any, tid: Long) { + getLockContext(lock).tryLockUnblocked(lock, tid) + } + // TODO(aoli): can we merge this logic with `objectNotifyImply`? fun objectWaitUnblockedWithoutNotify( waitingObject: Any, diff --git a/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/ReentrantLockContext.kt b/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/ReentrantLockContext.kt index 594af58c..389453e4 100644 --- a/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/ReentrantLockContext.kt +++ b/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/ReentrantLockContext.kt @@ -106,4 +106,8 @@ class ReentrantLockContext : LockContext { override fun isLockHolder(lock: Any, tid: Long): Boolean { return lockHolder == tid } + + override fun tryLockUnblocked(lock: Any, tid: Long) { + lockWaiters.remove(tid) + } } diff --git a/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/ReentrantReadWriteLockContext.kt b/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/ReentrantReadWriteLockContext.kt index 78f24abe..f93e5db1 100644 --- a/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/ReentrantReadWriteLockContext.kt +++ b/core/src/main/kotlin/org/pastalab/fray/core/concurrency/locks/ReentrantReadWriteLockContext.kt @@ -193,4 +193,12 @@ class ReentrantReadWriteLockContext : LockContext { writeLockHolder == tid } } + + override fun tryLockUnblocked(lock: Any, tid: Long) { + if (lock is ReadLock) { + readLockWaiters.remove(tid) + } else { + writeLockWaiters.remove(tid) + } + } } diff --git a/core/src/main/kotlin/org/pastalab/fray/core/concurrency/operations/LockBlocking.kt b/core/src/main/kotlin/org/pastalab/fray/core/concurrency/operations/LockBlocking.kt index 19207ff2..a3860d7b 100644 --- a/core/src/main/kotlin/org/pastalab/fray/core/concurrency/operations/LockBlocking.kt +++ b/core/src/main/kotlin/org/pastalab/fray/core/concurrency/operations/LockBlocking.kt @@ -1,3 +1,3 @@ package org.pastalab.fray.core.concurrency.operations -class LockBlocking(val lock: Any) : NonRacingOperation() {} +class LockBlocking(val lock: Any, timed: Boolean) : TimedBlockingOperation(timed) {} diff --git a/integration-test/src/main/java/org/pastalab/fray/test/success/lock/ReentrantLockTryLock.java b/integration-test/src/main/java/org/pastalab/fray/test/success/lock/ReentrantLockTryLock.java new file mode 100644 index 00000000..4f63a3c8 --- /dev/null +++ b/integration-test/src/main/java/org/pastalab/fray/test/success/lock/ReentrantLockTryLock.java @@ -0,0 +1,21 @@ +package org.pastalab.fray.test.success.lock; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; + +public class ReentrantLockTryLock { + public static void main(String[] args) throws InterruptedException { + ReentrantLock lock = new ReentrantLock(); + Thread t = new Thread(() -> { + lock.lock(); + Thread.yield(); + lock.unlock(); + }); + + t.start(); + Boolean result = lock.tryLock(1000, TimeUnit.MICROSECONDS); + assert(result == true); + Thread.yield(); + lock.unlock(); + } +} diff --git a/integration-test/src/test/java/org/pastalab/fray/test/FrayTestCase.java b/integration-test/src/test/java/org/pastalab/fray/test/FrayTestCase.java index 4cd3c211..5785aea7 100644 --- a/integration-test/src/test/java/org/pastalab/fray/test/FrayTestCase.java +++ b/integration-test/src/test/java/org/pastalab/fray/test/FrayTestCase.java @@ -19,6 +19,7 @@ import org.pastalab.fray.test.success.cdl.CountDownLatchNormalNotify; import org.pastalab.fray.test.success.condition.ConditionAwaitTimeoutInterrupt; import org.pastalab.fray.test.success.condition.ConditionAwaitTimeoutNotifyInterrupt; +import org.pastalab.fray.test.success.lock.ReentrantLockTryLock; import java.io.File; import java.util.*; @@ -72,7 +73,7 @@ public void testOne() throws Throwable { new ExecutionInfo( new LambdaExecutor(() -> { try { - CountDownLatchNormalNotify.main(new String[]{}); + ReentrantLockTryLock.main(new String[]{}); } catch (InterruptedException e) { throw new RuntimeException(e); } diff --git a/runtime/src/main/java/org/pastalab/fray/runtime/Delegate.java b/runtime/src/main/java/org/pastalab/fray/runtime/Delegate.java index bf1438ec..fab03397 100644 --- a/runtime/src/main/java/org/pastalab/fray/runtime/Delegate.java +++ b/runtime/src/main/java/org/pastalab/fray/runtime/Delegate.java @@ -44,10 +44,6 @@ public long onLockTryLockInterruptibly(Lock l, long timeout) { public void onLockTryLockInterruptiblyDone(Lock l) { } - public boolean onLockTryLockTimeout(Lock l, long timeout, TimeUnit unit) throws InterruptedException { - return l.tryLock(timeout, unit); - } - public void onLockTryLockDone(Lock l) { } diff --git a/runtime/src/main/java/org/pastalab/fray/runtime/Runtime.java b/runtime/src/main/java/org/pastalab/fray/runtime/Runtime.java index 210459ad..4b13a795 100644 --- a/runtime/src/main/java/org/pastalab/fray/runtime/Runtime.java +++ b/runtime/src/main/java/org/pastalab/fray/runtime/Runtime.java @@ -49,10 +49,6 @@ public static void onLockTryLockDone(Lock l) { DELEGATE.onLockTryLockDone(l); } - public static boolean onLockTryLockTimeout(Lock l, long timeout, TimeUnit unit) throws InterruptedException { - return DELEGATE.onLockTryLockTimeout(l, timeout, unit); - } - public static void onLockLock(Lock l) { DELEGATE.onLockLock(l); }