Skip to content

Commit 60cda09

Browse files
metanetmdogan
authored andcommitted
Make fenced lock re-entrancy non-local
Consider the following scenario: 1) client.lock(); Successful. 2) The client encounters a network partition 3) client.unlock(); Fails with operation timeout and also not executed on the server, but still local lock state is deleted. 4) Network partition issue is gone. The client still owns the lock. 5) client.getLockCount(); Returns 1, because client asks to the server. 6) client.lock(); Successful 7) client.getLockCount(); Returns 1, because client uses the local lock state to get the count, but the actual lock count is 2. Keeping local lock state is a difficult task and easily leads to problematic scenarios as above.
1 parent 135162e commit 60cda09

2 files changed

Lines changed: 15 additions & 84 deletions

File tree

hazelcast-raft-dataservices/src/main/java/com/hazelcast/raft/service/lock/proxy/AbstractRaftFencedLockProxy.java

Lines changed: 14 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,15 @@
1818

1919
import com.hazelcast.raft.RaftGroupId;
2020
import com.hazelcast.raft.impl.session.SessionExpiredException;
21+
import com.hazelcast.raft.service.exception.WaitKeyCancelledException;
2122
import com.hazelcast.raft.service.lock.FencedLock;
2223
import com.hazelcast.raft.service.lock.RaftLockService;
23-
import com.hazelcast.raft.service.exception.WaitKeyCancelledException;
2424
import com.hazelcast.raft.service.session.AbstractSessionManager;
2525
import com.hazelcast.raft.service.session.SessionAwareProxy;
2626
import com.hazelcast.spi.InternalCompletableFuture;
2727
import com.hazelcast.util.Clock;
2828

2929
import java.util.UUID;
30-
import java.util.concurrent.ConcurrentHashMap;
31-
import java.util.concurrent.ConcurrentMap;
3230
import java.util.concurrent.TimeUnit;
3331

3432
import static com.hazelcast.raft.service.lock.RaftLockService.INVALID_FENCE;
@@ -43,8 +41,6 @@
4341
public abstract class AbstractRaftFencedLockProxy extends SessionAwareProxy implements FencedLock {
4442

4543
protected final String name;
46-
// thread id -> lock state
47-
private final ConcurrentMap<Long, LockState> lockStates = new ConcurrentHashMap<Long, LockState>();
4844

4945
public AbstractRaftFencedLockProxy(AbstractSessionManager sessionManager, RaftGroupId groupId, String name) {
5046
super(sessionManager, groupId);
@@ -54,18 +50,11 @@ public AbstractRaftFencedLockProxy(AbstractSessionManager sessionManager, RaftGr
5450
@Override
5551
public final long lock() {
5652
long threadId = getThreadId();
57-
long fence = tryReentrantLock(threadId);
58-
if (fence != INVALID_FENCE) {
59-
return fence;
60-
}
61-
6253
UUID invocationUid = newUnsecureUUID();
6354
for (;;) {
6455
long sessionId = acquireSession();
6556
try {
66-
fence = doLock(groupId, name, sessionId, threadId, invocationUid).join();
67-
lockStates.put(threadId, new LockState(sessionId, fence));
68-
return fence;
57+
return doLock(groupId, name, sessionId, threadId, invocationUid).join();
6958
} catch (SessionExpiredException e) {
7059
invalidateSession(sessionId);
7160
}
@@ -82,22 +71,15 @@ public final long tryLock(long time, TimeUnit unit) {
8271
checkNotNull(unit);
8372

8473
long threadId = getThreadId();
85-
long fence = tryReentrantLock(threadId);
86-
if (fence != INVALID_FENCE) {
87-
return fence;
88-
}
89-
9074
UUID invocationUid = newUnsecureUUID();
9175
long timeoutMillis = Math.max(0, unit.toMillis(time));
9276
long start;
9377
for (;;) {
9478
start = Clock.currentTimeMillis();
9579
long sessionId = acquireSession();
9680
try {
97-
fence = doTryLock(groupId, name, sessionId, threadId, invocationUid, timeoutMillis).join();
98-
if (fence != INVALID_FENCE) {
99-
lockStates.put(threadId, new LockState(sessionId, fence));
100-
} else {
81+
long fence = doTryLock(groupId, name, sessionId, threadId, invocationUid, timeoutMillis).join();
82+
if (fence == INVALID_FENCE) {
10183
releaseSession(sessionId);
10284
}
10385
return fence;
@@ -113,62 +95,28 @@ public final long tryLock(long time, TimeUnit unit) {
11395
}
11496
}
11597

116-
private long tryReentrantLock(long threadId) {
117-
LockState lockState = lockStates.get(threadId);
118-
if (lockState != null) {
119-
if (lockState.sessionId == getSession()) {
120-
lockState.lockCount++;
121-
return lockState.fence;
122-
}
123-
lockStates.remove(threadId);
124-
throw new IllegalMonitorStateException("Current thread is not owner of the Lock[" + name + "] because Session["
125-
+ lockState.sessionId + "] is closed by server!");
126-
}
127-
return INVALID_FENCE;
128-
}
129-
13098
@Override
13199
public final void unlock() {
132100
long sessionId = getSession();
133101
if (sessionId == NO_SESSION_ID) {
134102
throw new IllegalMonitorStateException("Current thread is not owner of the Lock[" + name
135103
+ "] because session not found!");
136104
}
137-
long threadId = getThreadId();
138-
LockState lockState = lockStates.get(threadId);
139-
if (lockState == null) {
140-
throw new IllegalMonitorStateException("Current thread is not owner of the Lock[" + name + "]");
141-
}
142-
if (lockState.sessionId != sessionId) {
143-
lockStates.remove(threadId);
144-
throw new IllegalMonitorStateException("Current thread is not owner of the Lock[" + name + "] because Session["
145-
+ lockState.sessionId + "] is closed by server!");
146-
}
147-
if (lockState.lockCount > 1) {
148-
lockState.lockCount--;
149-
return;
150-
}
151-
152105
try {
153-
doUnlock(groupId, name, sessionId, threadId, newUnsecureUUID()).join();
106+
doUnlock(groupId, name, sessionId, getThreadId(), newUnsecureUUID()).join();
154107
} catch (SessionExpiredException e) {
155108
invalidateSession(sessionId);
156109
throw new IllegalMonitorStateException("Current thread is not owner of the Lock[" + name + "] because Session["
157110
+ sessionId + "] is closed by server!");
158111
} finally {
159-
lockStates.remove(threadId);
160112
releaseSession(sessionId);
161113
}
162114
}
163115

164116
@Override
165117
public final void forceUnlock() {
166-
try {
167-
long fence = doGetLockFence(groupId, name, NO_SESSION_ID, 0).join();
168-
doForceUnlock(groupId, name, fence, newUnsecureUUID()).join();
169-
} finally {
170-
lockStates.remove(getThreadId());
171-
}
118+
long fence = doGetLockFence(groupId, name, NO_SESSION_ID, 0).join();
119+
doForceUnlock(groupId, name, fence, newUnsecureUUID()).join();
172120
}
173121

174122
@Override
@@ -178,12 +126,7 @@ public final long getFence() {
178126
throw new IllegalMonitorStateException();
179127
}
180128

181-
LockState lockState = lockStates.get(getThreadId());
182-
if (lockState == null) {
183-
throw new IllegalMonitorStateException();
184-
}
185-
186-
return lockState.fence;
129+
return doGetLockFence(groupId, name, sessionId, getThreadId()).join();
187130
}
188131

189132
@Override
@@ -193,17 +136,16 @@ public final boolean isLocked() {
193136

194137
@Override
195138
public final boolean isLockedByCurrentThread() {
196-
LockState lockState = lockStates.get(getThreadId());
197-
return (lockState != null && lockState.sessionId == getSession());
139+
long sessionId = getSession();
140+
if (sessionId == NO_SESSION_ID) {
141+
return false;
142+
}
143+
144+
return doGetLockCount(groupId, name, sessionId, getThreadId()).join() > 0;
198145
}
199146

200147
@Override
201148
public final int getLockCount() {
202-
LockState lockState = lockStates.get(getThreadId());
203-
if (lockState != null && lockState.sessionId == getSession()) {
204-
return lockState.lockCount;
205-
}
206-
207149
return doGetLockCount(groupId, name, NO_SESSION_ID, 0).join();
208150
}
209151

@@ -240,15 +182,4 @@ protected abstract InternalCompletableFuture<Long> doGetLockFence(RaftGroupId gr
240182
protected abstract InternalCompletableFuture<Integer> doGetLockCount(RaftGroupId groupId, String name, long sessionId,
241183
long threadId);
242184

243-
private static class LockState {
244-
final long sessionId;
245-
final long fence;
246-
int lockCount;
247-
248-
LockState(long sessionId, long fence) {
249-
this.sessionId = sessionId;
250-
this.fence = fence;
251-
this.lockCount = 1;
252-
}
253-
}
254185
}

hazelcast-raft-dataservices/src/test/java/com/hazelcast/raft/service/lock/RaftFencedLockBasicTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ public void run() {
450450
lock.lock();
451451

452452
assertTrue(lock.isLockedByCurrentThread());
453-
assertEquals(2, lock.getLockCount());
453+
assertEquals(1, lock.getLockCount());
454454
}
455455

456456
@Test(timeout = 60000)

0 commit comments

Comments
 (0)