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

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

There is a newer version: 34.0.0.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.*;

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> values() { return table.values(); } /** Returns locks that have been successfully acquired */ protected synchronized List> getLockInfo() { List> l=new ArrayList<>(); // table.forEach((key, value) -> value.keySet().forEach(owner -> l.add(new Tuple<>(key, owner)))); table.forEach((k,v) -> v.forEach((owner, cl) -> { if(cl.acquired && !cl.denied) l.add(new Tuple<>(k, owner)); })); return l; } protected synchronized List getPendingRequests(Address sender) { List list=new ArrayList<>(); // add the pending LOCK requests table.forEach((k,v) -> v.forEach((owner, cl) -> { if(!cl.acquired && !cl.denied) { Request req=new Request(Type.GRANT_LOCK, cl.name, owner, cl.timeout, cl.is_trylock).lockId(cl.lock_id); list.add(req); } })); // add the pending UNLOCK requests pending_release_reqs.forEach(cl -> { if(cl.acquired && !cl.denied) { Request req=new Request(Type.RELEASE_LOCK, cl.name, cl.owner, cl.timeout, cl.is_trylock) .lockId(cl.lock_id).sender(sender); list.add(req); } }); return list; } public String printLocks() { return table.values().stream().map(Map::values).flatMap(Collection::stream) .filter(cl -> cl.isHeld() && Objects.nonNull(cl.name)) .map(cl -> cl.name).collect(Collectors.joining(", ")); } 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(); } public void addToPendingReleaseRequests(ClientLock cl) { if(cl != null) pending_release_reqs.add(cl); } public void removeFromPendingReleaseRequests(ClientLock cl) { if(cl != null) pending_release_reqs.remove(cl); } } 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; return waitTime > 0 && await(waitTime, TimeUnit.MILLISECONDS); } 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); } } public 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; protected LockInfoResponse info_rsp; protected Address sender; public Request() { } public Request(Type type) { this.type=type; } public Request(Type type, String lock_name, Owner owner, long timeout) { this(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 Type getType() {return type;} public Request lockId(int lock_id) {this.lock_id=lock_id; return this;} public int lockId() {return lock_id;} public Request infoRsp(LockInfoResponse r) {this.info_rsp=r; return this;} public Address sender() {return this.sender;} public Request sender(Address sender) {this.sender=sender; return this;} @Override public void writeTo(DataOutput out) throws IOException { out.writeByte(type.ordinal()); Bits.writeString(lock_name,out); out.writeInt(lock_id); Util.writeStreamable(owner, out); out.writeLong(timeout); out.writeBoolean(is_trylock); Util.writeStreamable(info_rsp, out); Util.writeAddress(sender, out); } @Override public void readFrom(DataInput in) throws IOException, ClassNotFoundException { type=Type.values()[in.readByte()]; lock_name=Bits.readString(in); lock_id=in.readInt(); owner=Util.readStreamable(Owner::new, in); timeout=in.readLong(); is_trylock=in.readBoolean(); info_rsp=Util.readStreamable(LockInfoResponse::new, in); sender=Util.readAddress(in); } public String toString() { StringBuilder sb=new StringBuilder(type.name() + "["); if(lock_name != null) sb.append(lock_name); if(lock_id > 0) sb.append(", lock_id=").append(lock_id); if(owner != null) sb.append(", owner=").append(owner); if(is_trylock) sb.append(", trylock"); if(timeout > 0) sb.append(", timeout=").append(timeout); if(sender != null) sb.append(", sender=").append(sender); sb.append("]"); return sb.toString(); } 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); if(info_rsp != null) sb.append(", lock-info-response: ").append(info_rsp); 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 short getMagicId() {return 72;} public Supplier create() { return LockingHeader::new; } @Override public int serializedSize() { return 0; } @Override public void writeTo(DataOutput out) throws IOException { } @Override public void readFrom(DataInput in) throws IOException, ClassNotFoundException { } } protected static class LockInfoResponse implements Streamable { protected List> existing_locks; // lock name and owner protected List pending_requests; protected LockInfoResponse add(Tuple el) { if(existing_locks == null) existing_locks=new ArrayList<>(); existing_locks.add(el); return this; } public void writeTo(DataOutput out) throws IOException { if(existing_locks == null) out.writeInt(0); else { out.writeInt(existing_locks.size()); for(Tuple t: existing_locks) { Bits.writeString(t.getVal1(), out); t.getVal2().writeTo(out); } } if(pending_requests == null) out.writeInt(0); else { out.writeInt(pending_requests.size()); for(Request req: pending_requests) req.writeTo(out); } } public void readFrom(DataInput in) throws IOException, ClassNotFoundException { int size=in.readInt(); if(size > 0) { existing_locks=new ArrayList<>(size); for(int i=0; i < size; i++) { String lock_name=Bits.readString(in); Owner owner=new Owner(); owner.readFrom(in); existing_locks.add(new Tuple<>(lock_name, owner)); } } size=in.readInt(); if(size > 0) { pending_requests=new ArrayList<>(); for(int i=0; i < size; i++) { Request req=new Request(); req.readFrom(in); pending_requests.add(req); } } } public String toString() { return String.format("%d locks and %d pending lock/unlock requests", existing_locks == null? 0 : existing_locks.size(), pending_requests == null? 0 : pending_requests.size()); } public String printDetails() { StringBuilder sb=new StringBuilder(toString()); if(existing_locks != null && !existing_locks.isEmpty()) sb.append(String.format("\nlocks:\n%s", existing_locks.stream().map(Tuple::getVal1) .collect(Collectors.joining(", ")))); if(pending_requests != null && !pending_requests.isEmpty()) sb.append(String.format("\npending requests:\n%s", pending_requests)); return sb.toString(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy