Skip to content

Commit

Permalink
Support timed tryLock. (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
aoli-al authored Nov 8, 2024
1 parent 98b2e50 commit e4ae7a3
Show file tree
Hide file tree
Showing 11 changed files with 65 additions and 28 deletions.
28 changes: 21 additions & 7 deletions core/src/main/kotlin/org/pastalab/fray/core/RunContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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]!!
Expand All @@ -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 = {
Expand All @@ -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
Expand All @@ -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) {
Expand Down
13 changes: 2 additions & 11 deletions core/src/main/kotlin/org/pastalab/fray/core/RuntimeDelegate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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")
Expand Down Expand Up @@ -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()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ interface LockContext : Interruptible {
fun isEmpty(): Boolean

fun isLockHolder(lock: Any, tid: Long): Boolean

fun tryLockUnblocked(lock: Any, tid: Long)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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) {}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;
Expand Down Expand Up @@ -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);
}
Expand Down
4 changes: 0 additions & 4 deletions runtime/src/main/java/org/pastalab/fray/runtime/Delegate.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
}

Expand Down
4 changes: 0 additions & 4 deletions runtime/src/main/java/org/pastalab/fray/runtime/Runtime.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit e4ae7a3

Please sign in to comment.