org.jgroups.protocols.Locking Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
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