All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgroups.protocols.Locking Maven / Gradle / Ivy

There is a newer version: 9.1.7.Final
Show newest version
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.Bits;
import org.jgroups.util.Owner;
import org.jgroups.util.Streamable;
import org.jgroups.util.Util;

import java.io.DataInput;
import java.io.DataOutput;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
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;


/**
 * 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
 */
@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;


    protected Address local_addr;

    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);
    


    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 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=(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();
                }
                return null;
            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());
                return null;
            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();
                log.trace("[%s] <-- [%s] %s", 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.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;
                    default:
                        log.error("Request of type %s not known", req.type);
                        break;
                }
                return null;

            case Event.VIEW_CHANGE:
                handleView((View)evt.getArg());
                break;
        }
        return up_prot.up(evt);
    }

    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="Dumps all locks")
    public String printLocks() {
        StringBuilder sb=new StringBuilder();
        sb.append("server locks:\n");
        for(Map.Entry entry: server_locks.entrySet())
            sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");

        sb.append("\nmy locks: ").append(client_lock_table.toString());
        return sb.toString();
    }

    protected void handleView(View view) {
        this.view=view;
        log.debug("view=%s", 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.current_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, 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) { // Message msg=new Message(dest, req).putHeader(id, new LockingHeader()).setFlag(Message.Flag.OOB); Message msg=new Message(dest, req).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(new Event(Event.MSG, msg)); } catch(Exception ex) { log.error("failed sending %s request: %s", 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.current_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 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 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.current_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.current_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("failed notifying %s: %s", listener, t.toString()); } } } protected void notifyLockDeleted(String lock_name) { for(LockNotification listener: lock_listeners) { try { listener.lockDeleted(lock_name); } catch(Throwable t) { log.error("failed notifying %s: %s", 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("failed notifying %s: %s", 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("failed notifying %s: %s", 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("failed notifying %s: %s", 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("failed notifying %s: %s", 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 current_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.current_owner=owner; this.condition=new ServerCondition(this); } protected Response handleRequest(Request req) { switch(req.type) { case GRANT_LOCK: if(current_owner == null) { setOwner(req.owner); return new Response(Type.LOCK_GRANTED, req.owner, req.lock_name, req.lock_id); } if(current_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(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"); } return processQueue(); } protected Response handleView(List
members) { if(current_owner != null && !members.contains(current_owner.getAddress())) { Owner tmp=current_owner; setOwner(null); log.debug("unlocked \"%s\" because owner %s left", lock_name, tmp); } synchronized(queue) { for(Iterator it=queue.iterator(); it.hasNext(); ) { Request req=it.next(); if(!members.contains(req.owner.getAddress())) it.remove(); } } for(Iterator it=condition.queue.iterator(); it.hasNext();) { Owner own=it.next(); if(!members.contains(own.getAddress())) it.remove(); } 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) { for(Iterator it=queue.iterator(); it.hasNext(); ) { Request req=it.next(); if(req.type == type && req.owner.equals(owner)) it.remove(); } } } protected Request getNextRequest() { synchronized(queue) { return !queue.isEmpty()? queue.remove(0) : null; } } protected Response processQueue() { if(current_owner != null) return null; Request req; while((req=getNextRequest()) != null) { if(req.type == Type.GRANT_LOCK) { setOwner(req.owner); return new Response(Type.LOCK_GRANTED, req.owner, req.lock_name, req.lock_id); } } return null; } 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() { synchronized(queue) { return queue.isEmpty(); } } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(current_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("Waiter [%s] was added for %s", waiter, lock.lock_name); queue.add(waiter); } public void removeWaiter(Owner waiter) { notifyAwaited(lock.lock_name, waiter); log.trace("Waiter [%s] was removed for %s", waiter, lock.lock_name); queue.remove(waiter); } public void signal(boolean all) { if (queue.isEmpty()) log.trace("Signal for [%s] ignored since, no one is waiting in queue", lock.lock_name); Owner entry; if (all) { while ((entry = queue.poll()) != null) { notifyAwaited(lock.lock_name, entry); log.trace("Signalled %s for %s", entry, lock.lock_name); sendSignalResponse(entry, lock.lock_name); } } else { entry = queue.poll(); if (entry != null) { notifyAwaited(lock.lock_name, entry); log.trace("Signalled %s for %s", 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 { 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 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 name + " (id=" + lock_id + ", locked=" + acquired + ")"; } 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 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) sendReleaseLockRequest(name, owner); acquired=denied=false; notifyAll(); 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; } } /** Manages access to client locks */ protected class ClientLockTable { protected final ConcurrentMap> table=Util.createConcurrentMap(20); 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=new HashMap<>(); 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 void removeClientLock(String lock_name, Owner owner) { Map owners=table.get(lock_name); if(owners != null) { ClientLock lock=owners.remove(owner); if(lock != null) { if(owners.isEmpty()) table.remove(lock_name); } } } protected void unlockAll() { List lock_list=new ArrayList<>(); synchronized(this) { Collection> maps=table.values(); for(Map map: maps) lock_list.addAll(map.values()); } for(ClientLock lock: lock_list) lock.unlock(); } protected void resendPendingLockRequests() { if(!table.isEmpty()) { for(Map map: table.values()) { for(ClientLock lock: map.values()) { if(!lock.acquired && !lock.denied) sendGrantLockRequest(lock.name, lock.lock_id, lock.owner, lock.timeout, lock.is_trylock); } } } } protected synchronized Collection> values() { return table.values(); } public String toString() { StringBuilder sb=new StringBuilder(); boolean first_element=true; for(Map.Entry> entry: table.entrySet()) { if(first_element) first_element=false; else sb.append(", "); sb.append(entry.getKey()).append(" ("); Map owners=entry.getValue(); boolean first=true; for(Map.Entry 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 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 parker=new AtomicReference<>(); 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 { InterruptedException ex = null; try { return await(nanosTimeout); } catch (InterruptedException e) { ex = e; throw ex; } finally { lock.lock(); // contract mandates we need to re-acquire the lock (see overridden method) // If we are throwing an InterruptedException then clear the interrupt state as well if (ex != null) Thread.interrupted(); } } /** * 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 (2263 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); } // Return the estimated time to wait (in ns), can be negative protected long await(long nanoSeconds) throws InterruptedException { long start=System.nanoTime(); 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=nanoSeconds - (System.nanoTime() - start); // 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 nanoSeconds - (System.nanoTime() - start); } @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 int lock_id; protected Owner owner; protected long timeout; 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 Request lockId(int lock_id) {this.lock_id=lock_id; return this;} public int lockId() {return lock_id;} public void writeTo(DataOutput out) throws Exception { out.writeByte(type.ordinal()); Bits.writeString(lock_name,out); out.writeInt(lock_id); Util.writeStreamable(owner, out); out.writeLong(timeout); out.writeBoolean(is_trylock); } public void readFrom(DataInput in) throws Exception { type=Type.values()[in.readByte()]; lock_name=Bits.readString(in); lock_id=in.readInt(); owner=(Owner)Util.readStreamable(Owner.class, in); timeout=in.readLong(); is_trylock=in.readBoolean(); } public String toString() { return type.name() + " [" + lock_name + ", lock_id=" + lock_id + ", 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(); } } /** A response to a request, to be sent back to the requester as a message */ protected static class Response { protected final Type type; protected final Owner owner; protected final String lock_name; protected final int lock_id; public Response(Type type, Owner owner, String lock_name, int lock_id) { this.type=type; this.owner=owner; this.lock_name=lock_name; this.lock_id=lock_id; } } public static class LockingHeader extends Header { public LockingHeader() { } public int size() { return 0; } public void writeTo(DataOutput out) throws Exception { } public void readFrom(DataInput in) throws Exception { } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy