use of org.neo4j.kernel.impl.locking.LockWaitEvent in project neo4j by neo4j.
the class ForsetiClient method acquireShared.
@Override
public void acquireShared(LockTracer tracer, ResourceType resourceType, long... resourceIds) throws AcquireLockTimeoutException {
hasLocks = true;
stateHolder.incrementActiveClients(this);
try {
// Grab the global lock map we will be using
ConcurrentMap<Long, ForsetiLockManager.Lock> lockMap = lockMaps[resourceType.typeId()];
// And grab our local lock maps
PrimitiveLongIntMap heldShareLocks = sharedLockCounts[resourceType.typeId()];
PrimitiveLongIntMap heldExclusiveLocks = exclusiveLockCounts[resourceType.typeId()];
for (long resourceId : resourceIds) {
// First, check if we already hold this as a shared lock
int heldCount = heldShareLocks.get(resourceId);
if (heldCount != -1) {
// We already have a lock on this, just increment our local reference counter.
heldShareLocks.put(resourceId, Math.incrementExact(heldCount));
continue;
}
// Second, check if we hold it as an exclusive lock
if (heldExclusiveLocks.containsKey(resourceId)) {
// We already have an exclusive lock, so just leave that in place.
// When the exclusive lock is released, it will be automatically downgraded to a shared lock,
// since we bumped the share lock reference count.
heldShareLocks.put(resourceId, 1);
continue;
}
// We don't hold the lock, so we need to grab it via the global lock map
int tries = 0;
SharedLock mySharedLock = null;
long waitStartMillis = clock.millis();
LockWaitEvent waitEvent = null;
try {
// Retry loop
while (true) {
assertValid(waitStartMillis, resourceType, resourceId);
// Check if there is a lock for this entity in the map
ForsetiLockManager.Lock existingLock = lockMap.get(resourceId);
// No lock
if (existingLock == null) {
// Try to create a new shared lock
if (mySharedLock == null) {
mySharedLock = new SharedLock(this);
}
if (lockMap.putIfAbsent(resourceId, mySharedLock) == null) {
// Success, we now hold the shared lock.
break;
} else {
continue;
}
} else // Someone holds shared lock on this entity, try and get in on that action
if (existingLock instanceof SharedLock) {
if (((SharedLock) existingLock).acquire(this)) {
// Success!
break;
}
} else // Someone holds an exclusive lock on this entity
if (existingLock instanceof ExclusiveLock) {
// We need to wait, just let the loop run.
} else {
throw new UnsupportedOperationException("Unknown lock type: " + existingLock);
}
if (waitEvent == null) {
waitEvent = tracer.waitForLock(false, resourceType, resourceId);
}
applyWaitStrategy(resourceType, tries++);
// And take note of who we are waiting for. This is used for deadlock detection.
markAsWaitingFor(existingLock, resourceType, resourceId);
}
} finally {
if (waitEvent != null) {
waitEvent.close();
}
}
// Got the lock, no longer waiting for anyone.
clearWaitList();
// Make a local note about the fact that we now hold this lock
heldShareLocks.put(resourceId, 1);
}
} finally {
stateHolder.decrementActiveClients();
}
}
use of org.neo4j.kernel.impl.locking.LockWaitEvent in project neo4j by neo4j.
the class RWLock method acquireReadLock.
/**
* Tries to acquire read lock for a given transaction. If
* <CODE>this.writeCount</CODE> is greater than the currents tx's write
* count the transaction has to wait and the {@link RagManager#checkWaitOn}
* method is invoked for deadlock detection.
* <p/>
* If the lock can be acquired the lock count is updated on <CODE>this</CODE>
* and the transaction lock element (tle).
* Waiting for a lock can also be terminated. In that case waiting thread will be interrupted and corresponding
* {@link org.neo4j.kernel.impl.locking.community.RWLock.TxLockElement} will be marked as terminated.
* In that case lock will not be acquired and false will be return as result of acquisition
*
* @return true is lock was acquired, false otherwise
* @throws DeadlockDetectedException if a deadlock is detected
*/
synchronized boolean acquireReadLock(LockTracer tracer, Object tx) throws DeadlockDetectedException {
TxLockElement tle = getOrCreateLockElement(tx);
LockRequest lockRequest = null;
LockWaitEvent waitEvent = null;
// used to track do we need to add lock request to a waiting queue or we still have it there
boolean addLockRequest = true;
try {
tle.incrementRequests();
Thread currentThread = currentThread();
long lockAcquisitionTimeBoundary = clock.millis() + lockAcquisitionTimeoutMillis;
while (!tle.isTerminated() && (totalWriteCount > tle.writeCount)) {
assertNotExpired(lockAcquisitionTimeBoundary);
ragManager.checkWaitOn(this, tx);
if (addLockRequest) {
lockRequest = new LockRequest(tle, READ, currentThread);
waitingThreadList.addFirst(lockRequest);
}
if (waitEvent == null) {
waitEvent = tracer.waitForLock(false, resource.type(), resource.resourceId());
}
addLockRequest = waitUninterruptedly(lockAcquisitionTimeBoundary);
ragManager.stopWaitOn(this, tx);
}
if (!tle.isTerminated()) {
registerReadLockAcquired(tx, tle);
return true;
} else {
// if it was register before it will be cleaned up during standard lock release call
if (tle.requests == 1 && tle.isFree()) {
txLockElementMap.remove(tx);
}
return false;
}
} finally {
if (waitEvent != null) {
waitEvent.close();
}
cleanupWaitingListRequests(lockRequest, tle, addLockRequest);
// for cases when spurious wake up was the reason why we waked up, but also there
// was an interruption as described at 17.2 just clearing interruption flag
interrupted();
// if deadlocked, remove marking so lock is removed when empty
tle.decrementRequests();
unmark();
}
}
use of org.neo4j.kernel.impl.locking.LockWaitEvent in project neo4j by neo4j.
the class ExecutingQueryTest method shouldReportWaitTime.
@Test
public void shouldReportWaitTime() throws Exception {
// given
query.planningCompleted(new PlannerInfo("the-planner", "the-runtime", emptyList()));
// then
assertEquals("running", query.snapshot().status());
// when
clock.forward(10, TimeUnit.SECONDS);
try (LockWaitEvent event = lock("NODE", 17)) {
clock.forward(5, TimeUnit.SECONDS);
// then
QuerySnapshot snapshot = query.snapshot();
assertEquals("waiting", snapshot.status());
assertThat(snapshot.resourceInformation(), CoreMatchers.<Map<String, Object>>allOf(hasEntry("waitTimeMillis", 5_000L), hasEntry("resourceType", "NODE"), hasEntry(equalTo("resourceIds"), longArray(17))));
assertEquals(5_000, snapshot.waitTimeMillis());
}
{
QuerySnapshot snapshot = query.snapshot();
assertEquals("running", snapshot.status());
assertEquals(5_000, snapshot.waitTimeMillis());
}
// when
clock.forward(2, TimeUnit.SECONDS);
try (LockWaitEvent event = lock("RELATIONSHIP", 612)) {
clock.forward(1, TimeUnit.SECONDS);
// then
QuerySnapshot snapshot = query.snapshot();
assertEquals("waiting", snapshot.status());
assertThat(snapshot.resourceInformation(), CoreMatchers.<Map<String, Object>>allOf(hasEntry("waitTimeMillis", 1_000L), hasEntry("resourceType", "RELATIONSHIP"), hasEntry(equalTo("resourceIds"), longArray(612))));
assertEquals(6_000, snapshot.waitTimeMillis());
}
{
QuerySnapshot snapshot = query.snapshot();
assertEquals("running", snapshot.status());
assertEquals(6_000, snapshot.waitTimeMillis());
}
}
use of org.neo4j.kernel.impl.locking.LockWaitEvent in project neo4j by neo4j.
the class ExecutingQueryTest method shouldTransitionBetweenStates.
@Test
public void shouldTransitionBetweenStates() throws Exception {
// initial
assertEquals("planning", query.snapshot().status());
// when
query.planningCompleted(new PlannerInfo("the-planner", "the-runtime", emptyList()));
// then
assertEquals("running", query.snapshot().status());
// when
try (LockWaitEvent event = lock("NODE", 17)) {
// then
assertEquals("waiting", query.snapshot().status());
}
// then
assertEquals("running", query.snapshot().status());
// when
query.waitsForQuery(subQuery);
// then
assertEquals("waiting", query.snapshot().status());
// when
query.waitsForQuery(null);
// then
assertEquals("running", query.snapshot().status());
}
Aggregations