package org.jgroups.protocols;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
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 org.jgroups.Address;
import org.jgroups.Event;
import org.jgroups.Header;
import org.jgroups.Message;
import org.jgroups.View;
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.blocks.locking.Owner;
import org.jgroups.stack.Protocol;
import org.jgroups.util.Streamable;
import org.jgroups.util.Util;
/**
* 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 org.jgroups.protocols.CENTRAL_LOCK
* @see org.jgroups.protocols.PEER_LOCK
*/
@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;
protected Address local_addr;
protected View view;
// server side locks
protected final ConcurrentMap<String,ServerLock> server_locks=Util.createConcurrentMap(20);
// client side locks
protected final Map<String,Map<Owner,ClientLock>> client_locks=new HashMap<String,Map<Owner,ClientLock>>();
protected final Set<LockNotification> lock_listeners=new HashSet<LockNotification>();
protected static 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
CREATE_LOCK, // request to create a server lock (sent by coordinator to backups). Used by CentralLockService
DELETE_LOCK, // request to delete a server lock (sent by coordinator to backups). Used by CentralLockService
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 CentralLockService
DELETE_AWAITER // request to delete a server lock await (sent by coordinator to backups). Used by CentralLockService
}
public Locking() {
}
public boolean getBypassBundling() {
return bypass_bundling;
}
public void setBypassBundling(boolean bypass_bundling) {
this.bypass_bundling=bypass_bundling;
}
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 getAddress() {
return local_addr != null? local_addr.toString() : null;
}
@ManagedAttribute
public String getView() {
return view != null? view.toString() : null;
}
public Object down(Event evt) {
switch(evt.getType()) {
case Event.LOCK:
LockInfo info=(LockInfo)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=(LockInfo)evt.getArg();
lock=getLock(info.getName(), false);
if(lock != null)
lock.unlock();
return null;
case Event.UNLOCK_ALL:
unlockAll();
return null;
case Event.LOCK_AWAIT:
info=(LockInfo)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();
}
break;
case Event.LOCK_SIGNAL:
AwaitInfo awaitInfo = (AwaitInfo)evt.getArg();
lock=getLock(awaitInfo.getName(), false);
if (lock == null || !lock.acquired) {
throw new IllegalMonitorStateException();
}
sendSignalConditionRequest(awaitInfo.getName(),
awaitInfo.isAll());
break;
case Event.SET_LOCAL_ADDRESS:
local_addr=(Address)evt.getArg();
break;
case Event.VIEW_CHANGE:
handleView((View)evt.getArg());
break;
}
return down_prot.down(evt);
}
public Object up(Event evt) {
switch(evt.getType()) {
case Event.MSG:
Message msg=(Message)evt.getArg();
LockingHeader hdr=(LockingHeader)msg.getHeader(id);
if(hdr == null)
break;
Request req=(Request)msg.getObject();
if(log.isTraceEnabled())
log.trace("[" + local_addr + "] <-- [" + msg.getSrc() + "] " + req);
switch(req.type) {
case GRANT_LOCK:
case RELEASE_LOCK:
handleLockRequest(req);
break;
case LOCK_GRANTED:
handleLockGrantedResponse(req.lock_name, req.owner, msg.getSrc());
break;
case LOCK_DENIED:
handleLockDeniedResponse(req.lock_name, 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;
default:
log.error("Request of type " + req.type + " not known");
break;
}
return null;
case Event.VIEW_CHANGE:
handleView((View)evt.getArg());
break;
}
return up_prot.up(evt);
}
protected ClientLock getLock(String name) {
return getLock(name, getOwner(), true);
}
protected ClientLock getLock(String name, boolean create_if_absent) {
return getLock(name, getOwner(), create_if_absent);
}
@ManagedOperation(description="Unlocks all currently held locks")
public void unlockAll() {
List<ClientLock> locks=new ArrayList<ClientLock>();
synchronized(client_locks) {
Collection<Map<Owner,ClientLock>> maps=client_locks.values();
for(Map<Owner,ClientLock> map: maps) {
locks.addAll(map.values());
}
}
for(ClientLock lock: locks)
lock.unlock();
}
@ManagedOperation(description="Dumps all locks")
public String printLocks() {
StringBuilder sb=new StringBuilder();
sb.append("server locks:\n");
for(Map.Entry<String,ServerLock> entry: server_locks.entrySet()) {
sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
}
sb.append("\nmy locks: ");
synchronized(client_locks) {
boolean first_element=true;
for(Map.Entry<String,Map<Owner,ClientLock>> entry: client_locks.entrySet()) {
if(first_element)
first_element=false;
else
sb.append(", ");
sb.append(entry.getKey()).append(" (");
Map<Owner,ClientLock> owners=entry.getValue();
boolean first=true;
for(Map.Entry<Owner,ClientLock> entry2: owners.entrySet()) {
if(first)
first=false;
else
sb.append(", ");
sb.append(entry2.getKey());
ClientLock cl=entry2.getValue();
if(!cl.acquired || cl.denied)
sb.append(", unlocked");
}
sb.append(")");
}
}
return sb.toString();
}
protected void handleView(View view) {
this.view=view;
if(log.isDebugEnabled())
log.debug("view=" + view);
List<Address> members=view.getMembers();
for(Map.Entry<String,ServerLock> entry: server_locks.entrySet()) {
entry.getValue().handleView(members);
}
for(Map.Entry<String,ServerLock> entry: server_locks.entrySet()) {
ServerLock lock=entry.getValue();
if(lock.isEmpty() && lock.current_owner == null)
server_locks.remove(entry.getKey());
}
}
protected ClientLock createLock(String lock_name) {
return new ClientLock(lock_name);
}
protected Owner getOwner() {
return new Owner(local_addr, Thread.currentThread().getId());
}
abstract protected void sendGrantLockRequest(String lock_name, Owner owner, long timeout, boolean is_trylock);
abstract protected void sendReleaseLockRequest(String lock_name, 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) {
Request req=new Request(type, lock_name, owner, timeout, is_trylock);
Message msg=new Message(dest, null, req);
msg.putHeader(id, new LockingHeader());
if(bypass_bundling)
msg.setFlag(Message.DONT_BUNDLE);
if(log.isTraceEnabled())
log.trace("[" + local_addr + "] --> [" + (dest == null? "ALL" : dest) + "] " + req);
try {
down_prot.down(new Event(Event.MSG, msg));
}
catch(Exception ex) {
log.error("failed sending " + type + " request: " + ex);
}
}
protected void sendLockResponse(Type type, Owner dest, String lock_name) {
Request rsp=new Request(type, lock_name, dest, 0);
Message lock_granted_rsp=new Message(dest.getAddress(), null, rsp);
lock_granted_rsp.putHeader(id, new LockingHeader());
if(bypass_bundling)
lock_granted_rsp.setFlag(Message.DONT_BUNDLE);
if(log.isTraceEnabled())
log.trace("[" + local_addr + "] --> [" + dest.getAddress() + "] " + rsp);
try {
down_prot.down(new Event(Event.MSG, lock_granted_rsp));
}
catch(Exception ex) {
log.error("failed sending " + type + " message to " + dest + ": " + ex);
}
}
protected void sendSignalResponse(Owner dest, String lock_name) {
Request rsp=new Request(Type.SIG_RET, lock_name, dest, 0);
Message lock_granted_rsp=new Message(dest.getAddress(), null, rsp);
lock_granted_rsp.putHeader(id, new LockingHeader());
if(bypass_bundling)
lock_granted_rsp.setFlag(Message.DONT_BUNDLE);
if(log.isTraceEnabled())
log.trace("[" + local_addr + "] --> [" + dest.getAddress() + "] " + rsp);
try {
down_prot.down(new Event(Event.MSG, lock_granted_rsp));
}
catch(Exception ex) {
log.error("failed sending " + Type.SIG_RET + " message to " + dest + ": " + ex);
}
}
protected void handleLockRequest(Request req) {
ServerLock lock=server_locks.get(req.lock_name);
if(lock == null) {
lock=new ServerLock(req.lock_name);
ServerLock tmp=server_locks.putIfAbsent(req.lock_name, lock);
if(tmp != null)
lock=tmp;
else {
notifyLockCreated(req.lock_name);
}
}
lock.handleRequest(req);
// We remove the lock if there is no waiters or owner
if(lock.isEmpty() && lock.current_owner == null && lock.condition.queue.isEmpty()) {
server_locks.remove(req.lock_name);
}
}
protected void handleLockGrantedResponse(String lock_name, Owner owner, Address sender) {
ClientLock lock=getLock(lock_name, owner, false);
if(lock != null)
lock.handleLockGrantedResponse(owner, sender);
}
protected void handleLockDeniedResponse(String lock_name, Owner owner) {
ClientLock lock=getLock(lock_name, owner, false);
if(lock != null)
lock.lockDenied();
}
protected void handleAwaitRequest(String lock_name, Owner owner) {
ServerLock lock=server_locks.get(lock_name);
if (lock != null) {
lock.condition.addWaiter(owner);
}
else {
log.error("Condition await was received but lock was not created. Waiter may block forever");
}
}
protected void handleDeleteAwaitRequest(String lock_name, Owner owner) {
ServerLock lock=server_locks.get(lock_name);
if (lock != null) {
lock.condition.removeWaiter(owner);
}
else {
log.error("Condition await delete was received, but lock was gone");
}
}
protected void handleSignalResponse(String lock_name, Owner owner) {
ClientLock lock=getLock(lock_name, owner, false);
if(lock != null) {
synchronized (lock.condition) {
lock.condition.signaled();
}
}
else {
log.error("Condition response was client lock was not present. Ignored signal.");
}
}
protected void handleSignalRequest(Request req) {
ServerLock lock=server_locks.get(req.lock_name);
if (lock != null) {
lock.handleRequest(req);
}
else {
log.error("Condition signal was received but lock was not created. Couldn't notify anyone.");
}
}
protected void handleCreateLockRequest(String lock_name, Owner owner) {
synchronized(server_locks) {
server_locks.put(lock_name, new ServerLock(lock_name, owner));
}
}
protected void handleDeleteLockRequest(String lock_name) {
synchronized(server_locks) {
ServerLock lock = server_locks.get(lock_name);
synchronized (lock.condition) {
if (lock.condition.queue.isEmpty()) {
server_locks.remove(lock_name);
}
else {
lock.current_owner = null;
}
}
}
}
protected void handleCreateAwaitingRequest(String lock_name, Owner owner) {
synchronized(server_locks) {
ServerLock lock = server_locks.get(lock_name);
if (lock == null) {
lock = new ServerLock(lock_name);
}
lock.condition.queue.add(owner);
}
}
protected void handleDeleteAwaitingRequest(String lock_name, Owner owner) {
synchronized(server_locks) {
ServerLock lock = server_locks.get(lock_name);
if (lock != null) {
synchronized (lock.condition) {
lock.condition.queue.remove(owner);
if (lock.condition.queue.isEmpty() && lock.current_owner == null) {
server_locks.remove(lock_name);
}
}
}
}
}
protected ClientLock getLock(String name, Owner owner, boolean create_if_absent) {
synchronized(client_locks) {
Map<Owner,ClientLock> owners=client_locks.get(name);
if(owners == null) {
if(!create_if_absent)
return null;
owners=new HashMap<Owner,ClientLock>();
client_locks.put(name, owners);
}
ClientLock lock=owners.get(owner);
if(lock == null) {
if(!create_if_absent)
return null;
lock=createLock(name);
owners.put(owner, lock);
}
return lock;
}
}
protected void removeClientLock(String lock_name, Owner owner) {
synchronized(client_locks) {
Map<Owner,ClientLock> owners=client_locks.get(lock_name);
if(owners != null) {
ClientLock lock=owners.remove(owner);
if(lock != null) {
if(owners.isEmpty())
client_locks.remove(lock_name);
}
}
}
}
protected void notifyLockCreated(String lock_name) {
for(LockNotification listener: lock_listeners) {
try {
listener.lockCreated(lock_name);
}
catch(Throwable t) {
log.error("failed notifying " + listener, t);
}
}
}
protected void notifyLockDeleted(String lock_name) {
for(LockNotification listener: lock_listeners) {
try {
listener.lockDeleted(lock_name);
}
catch(Throwable t) {
log.error("failed notifying " + listener, t);
}
}
}
protected void notifyLocked(String lock_name, Owner owner) {
for(LockNotification listener: lock_listeners) {
try {
listener.locked(lock_name, owner);
}
catch(Throwable t) {
log.error("failed notifying " + listener, t);
}
}
}
protected void notifyUnlocked(String lock_name, Owner owner) {
for(LockNotification listener: lock_listeners) {
try {
listener.unlocked(lock_name, owner);
}
catch(Throwable t) {
log.error("failed notifying " + listener, t);
}
}
}
protected void notifyAwaiting(String lock_name, Owner owner) {
for(LockNotification listener: lock_listeners) {
try {
listener.awaiting(lock_name, owner);
}
catch(Throwable t) {
log.error("failed notifying " + listener, t);
}
}
}
protected void notifyAwaited(String lock_name, Owner owner) {
for(LockNotification listener: lock_listeners) {
try {
listener.awaited(lock_name, owner);
}
catch(Throwable t) {
log.error("failed notifying " + listener, t);
}
}
}
/**
* Server side queue for handling of lock requests (lock, release).
* @author Bela Ban
*/
protected class ServerLock {
protected final String lock_name;
protected Owner current_owner;
protected final List<Request> queue=new ArrayList<Request>();
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.current_owner=owner;
this.condition=new ServerCondition(this);
}
protected synchronized void handleRequest(Request req) {
switch(req.type) {
case GRANT_LOCK:
if(current_owner == null) {
setOwner(req.owner);
sendLockResponse(Type.LOCK_GRANTED, req.owner, req.lock_name);
}
else {
if(current_owner.equals(req.owner)) {
sendLockResponse(Type.LOCK_GRANTED, req.owner, req.lock_name);
}
else {
if(req.is_trylock && req.timeout <= 0)
sendLockResponse(Type.LOCK_DENIED, req.owner, req.lock_name);
else
addToQueue(req);
}
}
break;
case RELEASE_LOCK:
case LOCK_AWAIT:
if(current_owner == null)
break;
if(current_owner.equals(req.owner))
setOwner(null);
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");
}
processQueue();
}
protected synchronized void handleView(List<Address> members) {
if(current_owner != null && !members.contains(current_owner.getAddress())) {
Owner tmp=current_owner;
setOwner(null);
if(log.isDebugEnabled())
log.debug("unlocked \"" + lock_name + "\" because owner " + tmp + " left");
}
for(Iterator<Request> it=queue.iterator(); it.hasNext();) {
Request req=it.next();
if(!members.contains(req.owner.getAddress()))
it.remove();
}
for(Iterator<Owner> it=condition.queue.iterator(); it.hasNext();) {
Owner own=it.next();
if(!members.contains(own.getAddress())) {
it.remove();
}
}
processQueue();
}
protected void addToQueue(Request req) {
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:
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) {
for(Request req: queue)
if(req.type == type && req.owner.equals(owner))
return true;
return false;
}
protected void removeRequest(Type type, Owner owner) {
for(Iterator<Request> it=queue.iterator(); it.hasNext();) {
Request req=it.next();
if(req.type == type && req.owner.equals(owner))
it.remove();
}
}
protected void processQueue() {
if(current_owner == null) {
while(!queue.isEmpty()) {
Request req=queue.remove(0);
if(req.type == Type.GRANT_LOCK) {
setOwner(req.owner);
sendLockResponse(Type.LOCK_GRANTED, req.owner, req.lock_name);
break;
}
}
}
}
protected void setOwner(Owner owner) {
if(owner == null) {
if(current_owner != null) {
Owner tmp=current_owner;
current_owner=null;
notifyUnlocked(lock_name, tmp);
}
}
else {
current_owner=owner;
notifyLocked(lock_name, owner);
}
}
public boolean isEmpty() {return queue.isEmpty();}
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append(current_owner);
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<Owner> queue=new ArrayDeque<Owner>();
public ServerCondition(ServerLock lock) {
this.lock = lock;
}
public synchronized void addWaiter(Owner waiter) {
notifyAwaiting(lock.lock_name, waiter);
if (log.isTraceEnabled()) {
log.trace("Waiter [" + waiter + "] was added for " + lock.lock_name);
}
queue.add(waiter);
}
public synchronized void removeWaiter(Owner waiter) {
notifyAwaited(lock.lock_name, waiter);
if (log.isTraceEnabled()) {
log.trace("Waiter [" + waiter + "] was removed for " + lock.lock_name);
}
queue.remove(waiter);
}
public synchronized void signal(boolean all) {
if (queue.isEmpty()) {
if (log.isTraceEnabled()) {
log.trace("Signal for [" + lock.lock_name +
"] ignored since, no one is waiting in queue.");
}
}
Owner entry;
if (all) {
while ((entry = queue.poll()) != null) {
notifyAwaited(lock.lock_name, entry);
if (log.isTraceEnabled()) {
log.trace("Signalled " + entry + " for " + lock.lock_name);
}
sendSignalResponse(entry, lock.lock_name);
}
}
else {
entry = queue.poll();
if (entry != null) {
notifyAwaited(lock.lock_name, entry);
if (log.isTraceEnabled()) {
log.trace("Signalled " + entry + " for " + lock.lock_name);
}
sendSignalResponse(entry, lock.lock_name);
}
}
}
}
protected class ClientLock implements Lock {
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;
public ClientLock(String name) {
this.name=name;
this.condition = new ClientCondition(this);
}
public void lock() {
try {
acquire(false);
}
catch(InterruptedException e) {
// This should never happen
}
}
public void lockInterruptibly() throws InterruptedException {
acquire(true);
}
public boolean tryLock() {
try {
return acquireTryLock(0, false);
}
catch(InterruptedException e) {
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 name + " (locked=" + acquired +")";
}
protected synchronized void lockGranted() {
acquired=true;
this.notifyAll();
}
protected synchronized void lockDenied() {
denied=true;
this.notifyAll();
}
protected void handleLockGrantedResponse(Owner owner, Address sender) {
lockGranted();
}
protected synchronized void acquire(boolean throwInterrupt) throws InterruptedException {
if(!acquired) {
owner=getOwner();
sendGrantLockRequest(name, owner, 0, false);
boolean interrupted=false;
while(!acquired) {
try {
this.wait();
}
catch (InterruptedException e) {
// If we haven't acquired the lock yet and were interrupted, then we have to clean up the lock
// request and throw the exception
if (throwInterrupt && !acquired) {
_unlock(true);
throw e;
}
// If we did get the lock then we will return with the lock and interrupt status.
// 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;
sendReleaseLockRequest(name, owner);
acquired=denied=false;
notifyAll();
removeClientLock(name, owner);
notifyLockDeleted(name);
owner=null;
}
protected synchronized boolean acquireTryLock(long timeout, boolean use_timeout) throws InterruptedException {
if(denied)
return false;
if(!acquired) {
is_trylock=true;
this.timeout=timeout;
owner=getOwner();
sendGrantLockRequest(name, owner, timeout, true);
long target_time=use_timeout? System.currentTimeMillis() + timeout : 0;
boolean interrupted = false;
while(!acquired && !denied) {
if(use_timeout) {
long wait_time=target_time - System.currentTimeMillis();
if(wait_time <= 0)
break;
else {
this.timeout=wait_time;
try {
this.wait(wait_time);
}
catch (InterruptedException e) {
// If we were interrupted and haven't received a response yet then we try to
// clean up the lock request and throw the exception
if (!acquired && !denied) {
_unlock(true);
throw e;
}
// In the case that we were told if we acquired or denied the lock then return that, but
// make sure we set the interrupt status
interrupted = true;
}
}
}
else {
try {
this.wait();
}
catch(InterruptedException e) {
interrupted = true;
}
}
}
if(interrupted)
Thread.currentThread().interrupt();
}
if(!acquired || denied)
_unlock(true);
return acquired && !denied;
}
}
protected class ClientCondition implements Condition {
protected final ClientLock lock;
protected final AtomicBoolean signaled = new AtomicBoolean(false);
/**
* This is okay only having 1 since a client condition is 1 per
* lock_name, thread id combination.
*/
protected volatile AtomicReference<Thread> parker=new AtomicReference<Thread>();
public ClientCondition(ClientLock lock) {
this.lock = lock;
}
@Override
public void await() throws InterruptedException {
InterruptedException ex = null;
try {
await(true);
}
catch (InterruptedException e) {
ex = e;
throw ex;
}
finally {
lock.lock();
// If we are throwing an InterruptedException
// then clear the interrupt state as well.
if (ex != null) {
Thread.interrupted();
}
}
}
@Override
public void awaitUninterruptibly() {
try {
await(false);
}
catch(InterruptedException e) {
// This should never happen
}
finally {
lock.lock();
}
}
@Override
public long awaitNanos(long nanosTimeout) throws InterruptedException {
long beforeLock;
InterruptedException ex = null;
try {
beforeLock = await(nanosTimeout) + System.nanoTime();
}
catch (InterruptedException e) {
ex = e;
throw ex;
}
finally {
lock.lock();
// If we are throwing an InterruptedException
// then clear the interrupt state as well.
if (ex != null) {
Thread.interrupted();
}
}
return beforeLock - System.nanoTime();
}
/**
* Note this wait will only work correctly if the converted value is less
* than 292 years. This is due to the limitation in System.nano and long
* values that can only store up to 292 years (22<sup>63</sup> nanoseconds).
*
* For more information please see {@link System#nanoTime()}
*/
@Override
public boolean await(long time, TimeUnit unit)
throws InterruptedException {
return awaitNanos(unit.toNanos(time)) > 0;
}
@Override
public boolean awaitUntil(Date deadline) throws InterruptedException {
long waitUntilTime = deadline.getTime();
long currentTime = System.currentTimeMillis();
long waitTime = waitUntilTime - currentTime;
if (waitTime > 0) {
return await(waitTime, TimeUnit.MILLISECONDS);
}
else {
return false;
}
}
protected void await(boolean throwInterrupt) throws InterruptedException {
if(!signaled.get()) {
lock.acquired = false;
sendAwaitConditionRequest(lock.name, lock.owner);
boolean interrupted=false;
while(!signaled.get()) {
parker.set(Thread.currentThread());
LockSupport.park(this);
if (Thread.interrupted()) {
// If we were interrupted and haven't received a response yet then we try to
// clean up the lock request and throw the exception
if (!signaled.get()) {
sendDeleteAwaitConditionRequest(lock.name, lock.owner);
throw new InterruptedException();
}
// In the case that we were signaled and interrupted
// we want to return the signal but still interrupt
// our thread
interrupted = true;
}
}
if(interrupted)
Thread.currentThread().interrupt();
}
// We set as if this signal was no released. This way if the
// condition is reused again, but the client condition isn't lost
// we won't think we were signaled immediately
signaled.set(false);
}
protected long await(long nanoSeconds) throws InterruptedException {
long target_nano=System.nanoTime() + nanoSeconds;
if(!signaled.get()) {
// We release the lock at the same time as waiting on the
// condition
lock.acquired = false;
sendAwaitConditionRequest(lock.name, lock.owner);
boolean interrupted = false;
while(!signaled.get()) {
long wait_nano=target_nano - System.nanoTime();
// If we waited max time break out
if(wait_nano > 0) {
parker.set(Thread.currentThread());
LockSupport.parkNanos(this, wait_nano);
if (Thread.interrupted()) {
// If we were interrupted and haven't received a response yet then we try to
// clean up the lock request and throw the exception
if (!signaled.get()) {
sendDeleteAwaitConditionRequest(lock.name, lock.owner);
throw new InterruptedException();
}
// In the case that we were signaled and interrupted
// we want to return the signal but still interrupt
// our thread
interrupted = true;
}
}
else {
break;
}
}
if(interrupted)
Thread.currentThread().interrupt();
}
// We set as if this signal was no released. This way if the
// condition is reused again, but the client condition isn't lost
// we won't think we were signaled immediately
// If we weren't signaled then delete our request
if (!signaled.getAndSet(false)) {
sendDeleteAwaitConditionRequest(lock.name, lock.owner);
}
return target_nano - System.nanoTime();
}
@Override
public void signal() {
sendSignalConditionRequest(lock.name, false);
}
@Override
public void signalAll() {
sendSignalConditionRequest(lock.name, true);
}
protected void signaled() {
signaled.set(true);
Thread thread = parker.getAndSet(null);
if (thread != null)
LockSupport.unpark(thread);
}
}
protected static class Request implements Streamable {
protected Type type;
protected String lock_name;
protected Owner owner;
protected long timeout=0;
protected boolean is_trylock;
public Request() {
}
public Request(Type type, String lock_name, Owner owner, long timeout) {
this.type=type;
this.lock_name=lock_name;
this.owner=owner;
this.timeout=timeout;
}
public Request(Type type, String lock_name, Owner owner, long timeout, boolean is_trylock) {
this(type, lock_name, owner, timeout);
this.is_trylock=is_trylock;
}
public void writeTo(DataOutput out) throws IOException {
out.writeByte(type.ordinal());
Util.writeString(lock_name, out);
Util.writeStreamable(owner, out);
out.writeLong(timeout);
out.writeBoolean(is_trylock);
}
public void readFrom(DataInput in) throws IOException, IllegalAccessException, InstantiationException {
type=Type.values()[in.readByte()];
lock_name=Util.readString(in);
owner=(Owner)Util.readStreamable(Owner.class, in);
timeout=in.readLong();
is_trylock=in.readBoolean();
}
public String toString() {
return type.name() + " [" + lock_name + ", owner=" + owner + (is_trylock? ", trylock " : " ") +
(timeout > 0? "(timeout=" + timeout + ")" : "" + "]");
}
public String toStringShort() {
StringBuilder sb=new StringBuilder();
switch(type) {
case RELEASE_LOCK:
sb.append("U");
break;
case GRANT_LOCK:
sb.append(is_trylock? "TL" : "L");
break;
default:
sb.append("N/A");
break;
}
sb.append("(").append(lock_name).append(",").append(owner);
if(timeout > 0)
sb.append(",").append(timeout);
sb.append(")");
return sb.toString();
}
}
public static class LockingHeader extends Header {
public LockingHeader() {
}
public int size() {
return 0;
}
public void writeTo(DataOutput out) throws IOException {
}
public void readFrom(DataInput in) throws IOException, IllegalAccessException, InstantiationException {
}
}
}