org.jgroups.protocols.Locking Maven / Gradle / Ivy
package org.jgroups.protocols;
import org.jgroups.*;
import org.jgroups.annotations.MBean;
import org.jgroups.annotations.ManagedAttribute;
import org.jgroups.annotations.ManagedOperation;
import org.jgroups.annotations.Property;
import org.jgroups.blocks.locking.AwaitInfo;
import org.jgroups.blocks.locking.LockInfo;
import org.jgroups.blocks.locking.LockNotification;
import org.jgroups.stack.Protocol;
import org.jgroups.util.*;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Base locking protocol, handling most of the protocol communication with other instances. To use distributed locking,
* {@link org.jgroups.blocks.locking.LockService} is placed on a channel. LockService talks to a subclass of Locking
* via events.
* @author Bela Ban
* @since 2.12
* @see CENTRAL_LOCK
* @see CENTRAL_LOCK2
* @deprecated See http://belaban.blogspot.com/2020/11/i-hate-distributed-locks.html.
*/
@Deprecated
@MBean(description="Based class for locking functionality")
abstract public class Locking extends Protocol {
@Property(description="bypasses message bundling if set")
protected boolean bypass_bundling=true;
@Property(description="Number of locks to be used for lock striping (for synchronized access to the server_lock entries)")
protected int lock_striping_size=10;
@Property(description="By default, a lock owner is address:thread-id. If false, we only use the node's address. " +
"See https://issues.redhat.com/browse/JGRP-1886 for details")
protected boolean use_thread_id_for_lock_owner=true;
protected View view;
// server side locks
protected final ConcurrentMap server_locks=Util.createConcurrentMap(20);
// protected access to the same locks in server_locks
protected Lock[] lock_stripes;
// client side locks
protected final ClientLockTable client_lock_table=new ClientLockTable();
protected final Set lock_listeners=new CopyOnWriteArraySet<>();
protected final static AtomicInteger current_lock_id=new AtomicInteger(1);
public enum Type {
GRANT_LOCK, // request to acquire a lock
LOCK_GRANTED, // response to sender of GRANT_LOCK on succcessful lock acquisition
LOCK_DENIED, // response to sender of GRANT_LOCK on unsuccessful lock acquisition (e.g. on tryLock())
RELEASE_LOCK, // request to release a lock
RELEASE_LOCK_OK, // response to RELEASE_LOCK request
CREATE_LOCK, // request to create a server lock (sent by coordinator to backups). Used by LockService
DELETE_LOCK, // request to delete a server lock (sent by coordinator to backups). Used by LockService
LOCK_AWAIT, // request to await until condition is signaled
COND_SIG, // request to signal awaiting thread
COND_SIG_ALL, // request to signal all awaiting threads
SIG_RET, // response to alert of signal
DELETE_LOCK_AWAIT, // request to delete a waiter
CREATE_AWAITER, // request to create a server lock await (sent by coordinator to backups). Used by LockService
DELETE_AWAITER, // request to delete a server lock await (sent by coordinator to backups). Used by LockService
LOCK_INFO_REQ, // request to get information about all acquired locks and all pending lock/unlock requests
LOCK_INFO_RSP, // response to LOCK_INFO_REQ
LOCK_REVOKED // sent on reconciliation when a lock is already present (possible on a merge when both sides hold the same lock)
}
public Locking() {
}
public boolean bypassBundling() {return bypass_bundling;}
public Locking bypassBundling(boolean b) {this.bypass_bundling=b; return this;}
public int getLockStripingSize() {return lock_striping_size;}
public Locking setLockStripingSize(int l) {this.lock_striping_size=l; return this;}
public boolean useThreadIdForLockOwner() {return use_thread_id_for_lock_owner;}
public Locking useThreadIdForLockOwner(boolean u) {this.use_thread_id_for_lock_owner=u; return this;}
public void addLockListener(LockNotification listener) {
if(listener != null)
lock_listeners.add(listener);
}
public void removeLockListener(LockNotification listener) {
if(listener != null)
lock_listeners.remove(listener);
}
@ManagedAttribute
public String getView() {
return view != null? view.toString() : null;
}
@ManagedAttribute(description="Number of server locks (only on coord)")
public int getNumServerLocks() {return server_locks.size();}
@ManagedAttribute(description="Number of client locks")
public int getNumClientLocks() {return client_lock_table.numLocks();}
public void init() throws Exception {
super.init();
lock_stripes=new Lock[lock_striping_size];
for(int i=0; i < lock_stripes.length; i++)
lock_stripes[i]=new ReentrantLock();
}
public Object down(Event evt) {
switch(evt.getType()) {
case Event.LOCK:
LockInfo info=evt.getArg();
ClientLock lock=getLock(info.getName());
if(!info.isTrylock()) {
if(info.isLockInterruptibly()) {
try {
lock.lockInterruptibly();
}
catch(InterruptedException e) {
Thread.currentThread().interrupt(); // has to be checked by caller who has to rethrow ...
}
}
else
lock.lock();
}
else {
if(info.isUseTimeout()) {
try {
return lock.tryLock(info.getTimeout(), info.getTimeUnit());
}
catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
}
else
return lock.tryLock();
}
return null;
case Event.UNLOCK:
info=evt.getArg();
lock=getLock(info.getName(), false);
if(lock != null)
lock.unlock();
return null;
case Event.UNLOCK_ALL:
unlockAll();
return null;
case Event.UNLOCK_FORCE:
unlockForce(evt.arg());
break;
case Event.LOCK_AWAIT:
info=evt.getArg();
lock=getLock(info.getName(), false);
if (lock == null || !lock.acquired) {
throw new IllegalMonitorStateException();
}
Condition condition = lock.newCondition();
if (info.isUseTimeout()) {
try {
return condition.awaitNanos(info.getTimeUnit().toNanos(info.getTimeout()));
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
else if (info.isLockInterruptibly()) {
try {
condition.await();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
else {
condition.awaitUninterruptibly();
}
return null;
case Event.LOCK_SIGNAL:
AwaitInfo awaitInfo =evt.getArg();
lock=getLock(awaitInfo.getName(), false);
if (lock == null || !lock.acquired) {
throw new IllegalMonitorStateException();
}
sendSignalConditionRequest(awaitInfo.getName(), awaitInfo.isAll());
return null;
case Event.VIEW_CHANGE:
handleView(evt.getArg());
break;
}
return down_prot.down(evt);
}
public Object up(Event evt) {
switch(evt.getType()) {
case Event.VIEW_CHANGE:
handleView(evt.getArg());
break;
}
return up_prot.up(evt);
}
public Object up(Message msg) {
LockingHeader hdr=msg.getHeader(id);
if(hdr == null)
return up_prot.up(msg);
Request req=null;
try {
req=Util.streamableFromBuffer(Request::new, msg.getArray(), msg.getOffset(), msg.getLength())
.sender(msg.getSrc());
}
catch(Exception ex) {
log.error("%s: failed deserializing request", local_addr, ex);
return null;
}
if(req.type != Type.LOCK_INFO_REQ && req.type != Type.LOCK_INFO_RSP && req.type != Type.LOCK_REVOKED
&& null != view && !view.containsMember(msg.getSrc())) {
log.error("%s: received request from '%s' but member is not present in the current view - ignoring request",
local_addr, msg.getSrc());
return null;
}
requestReceived(req);
return null;
}
protected void requestReceived(Request req) {
if(log.isTraceEnabled())
log.trace("%s <-- %s: %s", local_addr, req.sender, req);
handleRequest(req);
}
protected void handleRequest(Request req) {
if(req == null) return;
switch(req.type) {
case GRANT_LOCK:
case RELEASE_LOCK:
handleLockRequest(req);
break;
case LOCK_GRANTED:
handleLockGrantedResponse(req.lock_name, req.lock_id, req.owner);
break;
case RELEASE_LOCK_OK:
handleLockReleasedResponse(req.lock_name, req.lock_id, req.owner);
break;
case LOCK_DENIED:
handleLockDeniedResponse(req.lock_name, req.lock_id, req.owner);
break;
case CREATE_LOCK:
handleCreateLockRequest(req.lock_name, req.owner);
break;
case DELETE_LOCK:
handleDeleteLockRequest(req.lock_name);
break;
case COND_SIG:
case COND_SIG_ALL:
handleSignalRequest(req);
break;
case LOCK_AWAIT:
handleAwaitRequest(req.lock_name, req.owner);
handleLockRequest(req);
break;
case DELETE_LOCK_AWAIT:
handleDeleteAwaitRequest(req.lock_name, req.owner);
break;
case SIG_RET:
handleSignalResponse(req.lock_name, req.owner);
break;
case CREATE_AWAITER:
handleCreateAwaitingRequest(req.lock_name, req.owner);
break;
case DELETE_AWAITER:
handleDeleteAwaitingRequest(req.lock_name, req.owner);
break;
case LOCK_INFO_REQ:
handleLockInfoRequest(req.sender);
break;
case LOCK_INFO_RSP:
handleLockInfoResponse(req.sender, req);
break;
case LOCK_REVOKED:
handleLockRevoked(req);
break;
default:
log.error("%s: request of type %s not known", local_addr, req.type);
break;
}
}
protected ClientLock getLock(String name) {
return client_lock_table.getLock(name,getOwner(),true);
}
protected ClientLock getLock(String name, boolean create_if_absent) {
return client_lock_table.getLock(name,getOwner(),create_if_absent);
}
@ManagedOperation(description="Unlocks all currently held locks")
public void unlockAll() {
client_lock_table.unlockAll();
}
@ManagedOperation(description="Forcefully removes the client lock")
public void unlockForce(String lock_name) {
client_lock_table.unlockForce(lock_name);
}
@ManagedOperation(description="Dumps all locks")
public String printLocks() {
StringBuilder sb=new StringBuilder();
Collection values=server_locks.values();
if(values != null && !values.isEmpty()) {
sb.append("server locks: ");
for(ServerLock sl : server_locks.values())
sb.append(sl).append("\n");
}
String client_locks=client_lock_table.printLocks();
if(client_locks != null && !client_locks.isEmpty())
sb.append("my locks: ").append(client_lock_table.printLocks());
return sb.toString();
}
@ManagedOperation(description="Dumps all server locks")
public Object printServerLocks() {
return server_locks.values().stream().map(ServerLock::toString).collect(Collectors.joining(", "));
}
protected void handleView(View view) {
this.view=view;
log.debug("%s: view=%s", local_addr, view);
List members=view.getMembers();
List responses=new ArrayList<>();
for(Map.Entry entry: server_locks.entrySet()) {
String lock_name=entry.getKey();
ServerLock server_lock=entry.getValue();
Lock lock=_getLock(lock_name);
lock.lock();
try {
Response rsp=server_lock.handleView(members);
if(rsp != null)
responses.add(rsp);
if(server_lock.isEmpty() && server_lock.owner == null && server_lock.condition.queue.isEmpty())
server_locks.remove(lock_name);
}
finally {
lock.unlock();
}
}
// do the sending outside the lock scope (might block on credits or TCP send)
for(Response rsp: responses)
sendLockResponse(rsp.type, rsp.owner, rsp.lock_name, rsp.lock_id);
}
protected ClientLock createLock(String lock_name, Owner owner) {
return new ClientLock(lock_name, owner);
}
/** Gets a lock from locks based on the hash of the lock name */
protected Lock _getLock(String lock_name) {
int index=lock_name != null? Math.abs(lock_name.hashCode() % lock_stripes.length) : 0;
return lock_stripes[index];
}
protected Owner getOwner() {
return new Owner(local_addr, Thread.currentThread().getId());
}
abstract protected void sendGrantLockRequest(String lock_name, int lock_id, Owner owner, long timeout, boolean is_trylock);
abstract protected void sendReleaseLockRequest(String lock_name, int lock_id, Owner owner);
abstract protected void sendAwaitConditionRequest(String lock_name, Owner owner);
abstract protected void sendSignalConditionRequest(String lock_name, boolean all);
abstract protected void sendDeleteAwaitConditionRequest(String lock_name, Owner owner);
protected void sendRequest(Address dest, Type type, String lock_name, Owner owner, long timeout, boolean is_trylock) {
send(dest, new Request(type, lock_name, owner, timeout, is_trylock));
}
protected void sendRequest(Address dest, Type type, String lock_name, int lock_id, Owner owner, long timeout, boolean is_trylock) {
send(dest, new Request(type, lock_name, owner, timeout, is_trylock).lockId(lock_id));
}
protected void sendLockResponse(Type type, Owner dest, String lock_name, int lock_id) {
send(dest.getAddress(), new Request(type, lock_name, dest, 0).lockId(lock_id));
}
protected void sendSignalResponse(Owner dest, String lock_name) {
send(dest.getAddress(), new Request(Type.SIG_RET, lock_name, dest, 0));
}
protected void send(Address dest, Request req) {
ByteArray array=null;
try {
array=Util.streamableToBuffer(req);
}
catch(Exception e) {
log.warn("%s: failed serializing request: %s", local_addr, e);
}
Message msg=new BytesMessage(dest, array).putHeader(id, new LockingHeader());
if(bypass_bundling)
msg.setFlag(Message.Flag.DONT_BUNDLE);
log.trace("%s --> %s: %s", local_addr, dest == null? "ALL" : dest, req);
try {
down_prot.down(msg);
}
catch(Exception ex) {
log.error("%s: failed sending %s request: %s", local_addr, req.type, ex);
}
}
protected void handleLockRequest(Request req) {
Response rsp=null;
Lock lock=_getLock(req.lock_name);
lock.lock();
try {
ServerLock server_lock=server_locks.get(req.lock_name);
if(server_lock == null) {
server_lock=new ServerLock(req.lock_name);
ServerLock tmp=server_locks.putIfAbsent(req.lock_name, server_lock);
if(tmp != null)
server_lock=tmp;
else
notifyLockCreated(req.lock_name);
}
rsp=server_lock.handleRequest(req);
if(server_lock.isEmpty() && server_lock.owner == null && server_lock.condition.queue.isEmpty())
server_locks.remove(req.lock_name);
}
finally {
lock.unlock();
}
// moved outside the lock scope
if(rsp != null)
sendLockResponse(rsp.type, rsp.owner, rsp.lock_name, rsp.lock_id);
}
protected void handleLockGrantedResponse(String lock_name, int lock_id, Owner owner) {
ClientLock lock=client_lock_table.getLock(lock_name,owner,false);
if(lock != null)
lock.handleLockGrantedResponse(lock_id);
}
protected void handleLockReleasedResponse(String lock_name, int lock_id, Owner owner) {
ClientLock lock=client_lock_table.getLock(lock_name,owner,false);
if(lock != null)
lock.handleLockReleasedResponse(lock_id);
}
protected void handleLockDeniedResponse(String lock_name, int lock_id, Owner owner) {
ClientLock lock=client_lock_table.getLock(lock_name,owner,false);
if(lock != null)
lock.lockDenied(lock_id);
}
protected void handleLockInfoRequest(Address requester) {
}
protected void handleLockInfoResponse(Address sender, Request rsp) {
}
protected void handleLockRevoked(Request rsp) {
}
protected void handleAwaitRequest(String lock_name, Owner owner) {
Lock lock=_getLock(lock_name);
lock.lock();
try {
ServerLock server_lock=server_locks.get(lock_name);
if (server_lock != null)
server_lock.condition.addWaiter(owner);
else
log.error(Util.getMessage("ConditionAwaitWasReceivedButLockWasNotCreatedWaiterMayBlockForever"));
}
finally {
lock.unlock();
}
}
protected void handleDeleteAwaitRequest(String lock_name, Owner owner) {
Lock lock=_getLock(lock_name);
lock.lock();
try {
ServerLock server_lock=server_locks.get(lock_name);
if (server_lock != null)
server_lock.condition.removeWaiter(owner);
else
log.error(Util.getMessage("ConditionAwaitDeleteWasReceivedButLockWasGone"));
}
finally {
lock.unlock();
}
}
protected void handleSignalResponse(String lock_name, Owner owner) {
ClientLock lock=client_lock_table.getLock(lock_name,owner,false);
if(lock != null) {
lock.condition.signaled();
}
else {
log.error(Util.getMessage("ConditionResponseWasClientLockWasNotPresentIgnoredSignal"));
}
}
protected void handleSignalRequest(Request req) {
Response rsp=null;
Lock lock=_getLock(req.lock_name);
lock.lock();
try {
ServerLock server_lock=server_locks.get(req.lock_name);
if (server_lock != null)
rsp=server_lock.handleRequest(req);
else
log.error(Util.getMessage("ConditionSignalWasReceivedButLockWasNotCreatedCouldnTNotifyAnyone"));
}
finally {
lock.unlock();
}
// moved outside the lock scope
if(rsp != null)
sendLockResponse(rsp.type, rsp.owner, rsp.lock_name, rsp.lock_id);
}
protected void handleCreateLockRequest(String lock_name, Owner owner) {
Lock lock=_getLock(lock_name);
lock.lock();
try {
server_locks.put(lock_name, new ServerLock(lock_name, owner));
}
finally {
lock.unlock();
}
}
protected void handleDeleteLockRequest(String lock_name) {
Lock lock=_getLock(lock_name);
lock.lock();
try {
ServerLock server_lock = server_locks.get(lock_name);
if(server_lock == null)
return;
if (server_lock.condition.queue.isEmpty())
server_locks.remove(lock_name);
else
server_lock.owner= null;
}
finally {
lock.unlock();
}
}
protected void handleCreateAwaitingRequest(String lock_name, Owner owner) {
Lock lock=_getLock(lock_name);
lock.lock();
try {
ServerLock server_lock = server_locks.get(lock_name);
if (server_lock == null) {
server_lock = new ServerLock(lock_name);
ServerLock tmp=server_locks.putIfAbsent(lock_name,server_lock);
if(tmp != null)
server_lock=tmp;
}
server_lock.condition.queue.add(owner);
}
finally {
lock.unlock();
}
}
protected void handleDeleteAwaitingRequest(String lock_name, Owner owner) {
Lock lock=_getLock(lock_name);
lock.lock();
try {
ServerLock server_lock = server_locks.get(lock_name);
if (server_lock != null) {
server_lock.condition.queue.remove(owner);
if (server_lock.condition.queue.isEmpty() && server_lock.owner == null) {
server_locks.remove(lock_name);
}
}
}
finally {
lock.unlock();
}
}
protected void notifyLockCreated(String lock_name) {
for(LockNotification listener: lock_listeners) {
try {
listener.lockCreated(lock_name);
}
catch(Throwable t) {
log.error("%s: failed notifying %s: %s", local_addr, listener, t.toString());
}
}
}
protected void notifyLockDeleted(String lock_name) {
for(LockNotification listener: lock_listeners) {
try {
listener.lockDeleted(lock_name);
}
catch(Throwable t) {
log.error("%s: failed notifying %s: %s", local_addr, listener, t.toString());
}
}
}
protected void notifyLockRevoked(String lock_name, Owner current_owner) {
for(LockNotification listener: lock_listeners) {
try {
listener.lockRevoked(lock_name, current_owner);
}
catch(Throwable t) {
log.error("%s: failed notifying %s: %s", local_addr, listener, t.toString());
}
}
}
protected void notifyLocked(String lock_name, Owner owner) {
for(LockNotification listener: lock_listeners) {
try {
listener.locked(lock_name,owner);
}
catch(Throwable t) {
log.error("%s: failed notifying %s: %s", local_addr, listener, t.toString());
}
}
}
protected void notifyUnlocked(String lock_name, Owner owner) {
for(LockNotification listener: lock_listeners) {
try {
listener.unlocked(lock_name,owner);
}
catch(Throwable t) {
log.error("%s: failed notifying %s: %s", local_addr, listener, t.toString());
}
}
}
protected void notifyAwaiting(String lock_name, Owner owner) {
for(LockNotification listener: lock_listeners) {
try {
listener.awaiting(lock_name,owner);
}
catch(Throwable t) {
log.error("%s: failed notifying %s: %s", local_addr, listener, t.toString());
}
}
}
protected void notifyAwaited(String lock_name, Owner owner) {
for(LockNotification listener: lock_listeners) {
try {
listener.awaited(lock_name,owner);
}
catch(Throwable t) {
log.error("%s: failed notifying %s: %s", local_addr, listener, t.toString());
}
}
}
/**
* Server side queue for handling of lock requests (lock, release).
* @author Bela Ban
*/
protected class ServerLock {
protected final String lock_name;
protected Owner owner;
protected final List queue=new ArrayList<>();
protected final ServerCondition condition;
public ServerLock(String lock_name) {
this.lock_name=lock_name;
this.condition=new ServerCondition(this);
}
protected ServerLock(String lock_name, Owner owner) {
this.lock_name=lock_name;
this.owner=owner;
this.condition=new ServerCondition(this);
}
protected Response handleRequest(Request req) {
switch(req.type) {
case GRANT_LOCK:
if(owner == null) {
setOwner(req.owner);
return new Response(Type.LOCK_GRANTED, req.owner, req.lock_name, req.lock_id);
}
if(owner.equals(req.owner))
return new Response(Type.LOCK_GRANTED, req.owner, req.lock_name, req.lock_id);
if(req.is_trylock && req.timeout <= 0)
return new Response(Type.LOCK_DENIED, req.owner, req.lock_name, req.lock_id);
addToQueue(req);
break;
case RELEASE_LOCK:
case LOCK_AWAIT:
if(Objects.equals(owner, req.owner)) {
setOwner(null);
if(req.type == Type.RELEASE_LOCK)
sendLockResponse(Type.RELEASE_LOCK_OK, req.owner, req.lock_name, req.lock_id);
}
else
addToQueue(req);
break;
case COND_SIG:
condition.signal(false);
break;
case COND_SIG_ALL:
condition.signal(true);
break;
default:
throw new IllegalArgumentException("type " + req.type + " is invalid here");
}
return processQueue();
}
protected Response handleView(List members) {
if(owner != null && !members.contains(owner.getAddress())) {
Owner tmp=owner;
setOwner(null);
log.debug("%s: unlocked \"%s\" because owner %s left", local_addr, lock_name, tmp);
}
synchronized(queue) {
queue.removeIf(req -> !members.contains(req.owner.getAddress()));
}
condition.queue.removeIf(own -> !members.contains(own.getAddress()));
return processQueue();
}
protected void addToQueue(Request req) {
synchronized(queue) {
if(queue.isEmpty()) {
if(req.type == Type.GRANT_LOCK)
queue.add(req);
return; // RELEASE_LOCK is discarded on an empty queue
}
}
// at this point the queue is not empty
switch(req.type) {
// If there is already a lock request from the same owner, discard the new lock request
case GRANT_LOCK:
synchronized(queue) {
if(!isRequestPresent(Type.GRANT_LOCK, req.owner))
queue.add(req);
}
break;
case RELEASE_LOCK:
// Release the lock request from the same owner already in the queue
// If there is no lock request, discard the unlock request
removeRequest(Type.GRANT_LOCK, req.owner);
break;
}
}
/** Checks if a certain request from a given owner is already in the queue */
protected boolean isRequestPresent(Type type, Owner owner) { // holds lock on queue
for(Request req: queue)
if(req.type == type && req.owner.equals(owner))
return true;
return false;
}
protected void removeRequest(Type type, Owner owner) {
synchronized(queue) {
queue.removeIf(req -> req.type == type && req.owner.equals(owner));
}
}
protected Request getNextRequest() {
synchronized(queue) {
return !queue.isEmpty()? queue.remove(0) : null;
}
}
protected Response processQueue() {
if(owner != null)
return null;
Request req;
while((req=getNextRequest()) != null) {
switch(req.type) {
case GRANT_LOCK:
setOwner(req.owner);
return new Response(Type.LOCK_GRANTED, req.owner, req.lock_name, req.lock_id);
case RELEASE_LOCK:
if(owner == null)
break;
if(owner.equals(req.owner))
setOwner(null);
return new Response(Type.RELEASE_LOCK_OK, req.owner, req.lock_name, req.lock_id);
}
}
return null;
}
protected void setOwner(Owner owner) {
if(owner == null) {
if(this.owner != null) {
Owner tmp=this.owner;
this.owner=null;
notifyUnlocked(lock_name, tmp);
}
}
else {
this.owner=owner;
notifyLocked(lock_name, owner);
}
}
public boolean isEmpty() {
synchronized(queue) {
return queue.isEmpty();
}
}
public String toString() {
StringBuilder sb=new StringBuilder(lock_name + ": ").append(owner);
synchronized(queue) {
if(!queue.isEmpty()) {
sb.append(", queue: ");
for(Request req : queue) {
sb.append(req.toStringShort()).append(" ");
}
}
}
return sb.toString();
}
}
protected class ServerCondition {
protected final ServerLock lock;
protected final Queue queue=new ArrayDeque<>();
public ServerCondition(ServerLock lock) {
this.lock = lock;
}
public void addWaiter(Owner waiter) {
notifyAwaiting(lock.lock_name, waiter);
log.trace("%s: waiter %s was added for %s", local_addr, waiter, lock.lock_name);
queue.add(waiter);
}
public void removeWaiter(Owner waiter) {
notifyAwaited(lock.lock_name, waiter);
log.trace("%s: waiter %s was removed for %s", local_addr, waiter, lock.lock_name);
queue.remove(waiter);
}
public void signal(boolean all) {
if (queue.isEmpty())
log.trace("%s: signal for %s ignored since, no one is waiting in queue", local_addr, lock.lock_name);
Owner entry;
if (all) {
while ((entry = queue.poll()) != null) {
notifyAwaited(lock.lock_name, entry);
log.trace("%s: signalled %s for %s", local_addr, entry, lock.lock_name);
sendSignalResponse(entry, lock.lock_name);
}
}
else {
entry = queue.poll();
if (entry != null) {
notifyAwaited(lock.lock_name, entry);
log.trace("%s: signalled %s for %s", local_addr, entry, lock.lock_name);
sendSignalResponse(entry, lock.lock_name);
}
}
}
}
/**
* Implementation of {@link Lock}. This is a client stub communicates with a server equivalent. The semantics are
* more or less those of {@link Lock}, but may differ slightly.
* For details see {@link org.jgroups.blocks.locking.LockService}.
*/
protected class ClientLock implements Lock, Comparable {
protected final String name;
protected Owner owner;
protected volatile boolean acquired;
protected volatile boolean denied;
protected volatile boolean is_trylock;
protected long timeout;
protected final ClientCondition condition;
// unique for locks for the same name:owner, can wrap around (that's ok)
protected final int lock_id=current_lock_id.getAndIncrement();
public ClientLock(String name) {
this.name=name;
this.condition = new ClientCondition(this);
}
public ClientLock(String name, Owner owner) {
this(name);
this.owner=owner;
}
public boolean isHeld() {return acquired && !denied;}
public void lock() {
try {
acquire(false);
}
catch(InterruptedException e) { // should never happen
Thread.currentThread().interrupt(); // just a second line of defense
}
}
public void lockInterruptibly() throws InterruptedException {
acquire(true);
}
public boolean tryLock() {
try {
return acquireTryLock(0, false);
}
catch(InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return acquireTryLock(TimeUnit.MILLISECONDS.convert(time, unit), true);
}
public synchronized void unlock() {
_unlock(false);
}
public Condition newCondition() {
// Currently only 1 condition per Lock is supported
return condition;
}
public String toString() {
return String.format("%s (id=%d, locked=%b, owner=%s)", name, lock_id, acquired, owner != null? owner : "n/a");
}
protected synchronized void lockGranted(int lock_id) {
if(this.lock_id != lock_id) {
log.error(Util.getMessage("DiscardedLOCKGRANTEDResponseWithLockId") + lock_id + ", my lock-id=" + this.lock_id);
return;
}
acquired=true;
this.notifyAll();
}
protected synchronized void lockDenied(int lock_id) {
if(this.lock_id != lock_id) {
log.error(Util.getMessage("DiscardedLOCKDENIEDResponseWithLockId") + lock_id + ", my lock_id=" + this.lock_id);
return;
}
denied=true;
this.notifyAll();
}
protected void handleLockGrantedResponse(int lock_id) {
lockGranted(lock_id);
}
protected void handleLockReleasedResponse(int lock_id) {
if(this.lock_id != lock_id) {
log.error(Util.getMessage("DiscardedLOCKGRANTEDResponseWithLockId") + lock_id + ", my lock-id=" + this.lock_id);
return;
}
_unlockOK();
}
protected synchronized void acquire(boolean throwInterrupt) throws InterruptedException {
if(acquired)
return;
if(throwInterrupt && Thread.interrupted())
throw new InterruptedException();
owner=getOwner();
sendGrantLockRequest(name, lock_id, owner, 0, false);
boolean interrupted=false;
while(!acquired) {
try {
this.wait();
}
catch(InterruptedException e) {
if(throwInterrupt && !acquired) {
_unlock(true);
throw e;
}
// If we don't throw exceptions then we just set the interrupt flag and let it loop around
interrupted=true;
}
}
if(interrupted)
Thread.currentThread().interrupt();
}
protected synchronized void _unlock(boolean force) {
if(!acquired && !denied && !force)
return;
this.timeout=0;
this.is_trylock=false;
if(!denied) {
if(!force)
client_lock_table.addToPendingReleaseRequests(this);
sendReleaseLockRequest(name, lock_id, owner); // lock will be released on RELEASE_LOCK_OK response
if(force && client_lock_table.removeClientLock(name,owner))
notifyLockDeleted(name);
if(!force) {
//unlock will return only when get RELEASE_LOCK_OK or timeLeft after some seconds
long time_left=10000;
while(acquired || denied) {
long start=System.currentTimeMillis();
try {
wait(time_left);
}
catch(InterruptedException ie) {
break;
}
long duration=System.currentTimeMillis() - start;
if(duration > 0)
time_left-=duration;
if(time_left <= 0) {
log.warn("%s: timeout waiting for RELEASE_LOCK_OK response for lock %s", local_addr, this);
break;
}
}
}
}
else
_unlockOK();
}
protected synchronized void _unlockOK() {
acquired=denied=false;
notifyAll();
if(client_lock_table.removeClientLock(name,owner))
notifyLockDeleted(name);
owner=null;
}
protected synchronized boolean acquireTryLock(long timeout, boolean use_timeout) throws InterruptedException {
if(denied)
return false;
if(!acquired) {
if(use_timeout && Thread.interrupted())
throw new InterruptedException();
is_trylock=true;
this.timeout=timeout;
if(owner == null)
owner=getOwner();
sendGrantLockRequest(name, lock_id, owner, timeout, true);
boolean interrupted = false;
while(!acquired && !denied) {
if(use_timeout) {
long timeout_ns=TimeUnit.NANOSECONDS.convert(timeout, TimeUnit.MILLISECONDS),
wait_time=timeout_ns,
start=System.nanoTime();
while(wait_time > 0 && !acquired && !denied) {
try {
long wait_ms=TimeUnit.MILLISECONDS.convert(wait_time, TimeUnit.NANOSECONDS);
if(wait_ms <= 0)
break;
this.wait(wait_ms);
}
catch(InterruptedException e) {
interrupted=true;
}
finally {
wait_time=timeout_ns - (System.nanoTime() - start);
this.timeout=TimeUnit.MILLISECONDS.convert(wait_time, TimeUnit.NANOSECONDS);
}
}
break;
}
else {
try {
this.wait();
}
catch(InterruptedException e) {
interrupted = true;
}
}
}
if(interrupted)
Thread.currentThread().interrupt();
}
boolean retval=acquired && !denied;
if(!acquired || denied)
_unlock(true);
return retval;
}
public boolean equals(Object obj) {
return this == obj || Objects.equals(owner, ((ClientLock)obj).owner);
}
public int compareTo(ClientLock o) {
int rc=owner.compareTo(o.owner);
return rc != 0? rc : name.compareTo(o.name);
}
}
/** Manages access to client locks */
protected class ClientLockTable {
protected final ConcurrentMap> table=Util.createConcurrentMap(20);
protected final Set pending_release_reqs=new ConcurrentSkipListSet<>();
protected int numLocks() {return table.size();}
protected synchronized ClientLock getLock(String name, Owner owner, boolean create_if_absent) {
Map owners=table.get(name);
if(owners == null) {
if(!create_if_absent)
return null;
owners=Util.createConcurrentMap(20);
Map existing=table.putIfAbsent(name,owners);
if(existing != null)
owners=existing;
}
ClientLock lock=owners.get(owner);
if(lock == null) {
if(!create_if_absent)
return null;
lock=createLock(name, owner);
owners.put(owner, lock);
}
return lock;
}
protected synchronized boolean removeClientLock(String lock_name, Owner owner) {
pending_release_reqs.removeIf(cl -> Objects.equals(cl.name, lock_name) && Objects.equals(cl.owner, owner));
Map owners=table.get(lock_name);
if(owners != null) {
ClientLock lock=owners.remove(owner);
if(lock != null && owners.isEmpty())
table.remove(lock_name);
return lock != null;
}
return false;
}
protected void unlockAll() {
List lock_list=new ArrayList<>();
synchronized(this) {
table.values().forEach(map -> lock_list.addAll(map.values()));
}
lock_list.forEach(ClientLock::unlock);
}
protected void unlockForce(String lock_name) {
Map owners=table.get(lock_name);
if(owners != null) {
for(ClientLock cl : owners.values())
cl._unlock(true);
}
pending_release_reqs.removeIf(cl -> Objects.equals(cl.name, lock_name));
}
protected void resendPendingLockRequests() {
final List pending_lock_reqs=new ArrayList<>();
synchronized(this) {
if(!table.isEmpty()) {
table.values().forEach(map -> map.values().stream().filter(lock -> !lock.acquired && !lock.denied)
.forEach(pending_lock_reqs::add));
}
}
if(!pending_lock_reqs.isEmpty()) { // send outside of the synchronized block
if(log.isTraceEnabled()) {
String tmp=pending_lock_reqs.stream().map(ClientLock::toString).collect(Collectors.joining(", "));
log.trace("%s: resending pending lock requests: %s", local_addr, tmp);
}
pending_lock_reqs.forEach(l -> sendGrantLockRequest(l.name, l.lock_id, l.owner, l.timeout, l.is_trylock));
}
if(!pending_release_reqs.isEmpty()) {
if(log.isTraceEnabled()) {
String tmp=pending_release_reqs.stream().map(ClientLock::toString).collect(Collectors.joining(", "));
log.trace("%s: resending pending unlock requests: %s", local_addr, tmp);
}
pending_release_reqs.forEach(cl -> sendReleaseLockRequest(cl.name, cl.lock_id, cl.owner));
}
}
protected synchronized Collection
© 2015 - 2025 Weber Informatics LLC | Privacy Policy