X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-distributed-datastore%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fcluster%2Fdatastore%2Fentityownership%2FEntityOwnershipListenerSupport.java;h=0c4a646efef47b84cd22d37c24a50b5608c32cff;hb=refs%2Fchanges%2F28%2F81128%2F25;hp=be82582e06d2e356b4446e0e51d86c4f7a125f7b;hpb=83c901ab9309bda0f78e8a847a5511061f6e79b5;p=controller.git diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupport.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupport.java index be82582e06..0c4a646efe 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupport.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupport.java @@ -11,13 +11,16 @@ import akka.actor.ActorContext; import akka.actor.ActorRef; import akka.actor.PoisonPill; import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Map; -import java.util.Set; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.stream.Collectors; +import org.checkerframework.checker.lock.qual.GuardedBy; +import org.checkerframework.checker.lock.qual.Holding; import org.opendaylight.mdsal.eos.common.api.EntityOwnershipChangeState; import org.opendaylight.mdsal.eos.dom.api.DOMEntity; import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipChange; @@ -26,25 +29,32 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Manages EntityOwnershipListener registrations and notifications for the EntityOwnershipShard. + * Manages EntityOwnershipListener registrations and notifications for the EntityOwnershipShard. This class is + * thread-safe. * * @author Thomas Pantelis */ -class EntityOwnershipListenerSupport { +class EntityOwnershipListenerSupport extends EntityOwnershipChangePublisher { private static final Logger LOG = LoggerFactory.getLogger(EntityOwnershipListenerSupport.class); private final String logId; private final ActorContext actorContext; + private final ReadWriteLock listenerLock = new ReentrantReadWriteLock(); + + @GuardedBy("listenerLock") private final Map listenerActorMap = new IdentityHashMap<>(); - private final Set entitiesWithCandidateSet = new HashSet<>(); + + @GuardedBy("listenerLock") private final Multimap entityTypeListenerMap = HashMultimap.create(); + private volatile boolean inJeopardy = false; - EntityOwnershipListenerSupport(ActorContext actorContext, String logId) { + EntityOwnershipListenerSupport(final ActorContext actorContext, final String logId) { this.actorContext = actorContext; this.logId = logId; } + @Override String getLogId() { return logId; } @@ -55,58 +65,88 @@ class EntityOwnershipListenerSupport { * @param inJeopardy new value of the in-jeopardy flag * @return Previous value of the flag. */ + @SuppressWarnings("checkstyle:hiddenField") boolean setInJeopardy(final boolean inJeopardy) { final boolean wasInJeopardy = this.inJeopardy; this.inJeopardy = inJeopardy; return wasInJeopardy; } - boolean hasCandidateForEntity(DOMEntity entity) { - return entitiesWithCandidateSet.contains(entity); - } - - void setHasCandidateForEntity(DOMEntity entity) { - entitiesWithCandidateSet.add(entity); - } - - void unsetHasCandidateForEntity(DOMEntity entity) { - entitiesWithCandidateSet.remove(entity); - } - - void addEntityOwnershipListener(String entityType, DOMEntityOwnershipListener listener) { + void addEntityOwnershipListener(final String entityType, final DOMEntityOwnershipListener listener) { LOG.debug("{}: Adding EntityOwnershipListener {} for entity type {}", logId, listener, entityType); - addListener(listener, entityType); + listenerLock.writeLock().lock(); + try { + if (entityTypeListenerMap.put(entityType, listener)) { + ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener); + if (listenerEntry == null) { + listenerActorMap.put(listener, new ListenerActorRefEntry(listener)); + } else { + listenerEntry.referenceCount++; + } + } + } finally { + listenerLock.writeLock().unlock(); + } } - void removeEntityOwnershipListener(String entityType, DOMEntityOwnershipListener listener) { + void removeEntityOwnershipListener(final String entityType, final DOMEntityOwnershipListener listener) { LOG.debug("{}: Removing EntityOwnershipListener {} for entity type {}", logId, listener, entityType); - removeListener(listener, entityType); - } + listenerLock.writeLock().lock(); + try { + if (entityTypeListenerMap.remove(entityType, listener)) { + ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener); + + LOG.debug("{}: Found {}", logId, listenerEntry); - void notifyEntityOwnershipListeners(DOMEntity entity, boolean wasOwner, boolean isOwner, boolean hasOwner) { - notifyListeners(entity, entity.getType(), wasOwner, isOwner, hasOwner); + listenerEntry.referenceCount--; + if (listenerEntry.referenceCount <= 0) { + listenerActorMap.remove(listener); + + if (listenerEntry.actorRef != null) { + LOG.debug("Killing EntityOwnershipListenerActor {}", listenerEntry.actorRef); + listenerEntry.actorRef.tell(PoisonPill.getInstance(), ActorRef.noSender()); + } + } + } + } finally { + listenerLock.writeLock().unlock(); + } } - void notifyEntityOwnershipListener(DOMEntity entity, boolean wasOwner, boolean isOwner, boolean hasOwner, - DOMEntityOwnershipListener listener) { - notifyListeners(entity, wasOwner, isOwner, hasOwner, Collections.singleton(listener)); + @Override + void notifyEntityOwnershipListeners(final DOMEntity entity, final boolean wasOwner, final boolean isOwner, + final boolean hasOwner) { + listenerLock.readLock().lock(); + try { + Collection listeners = entityTypeListenerMap.get(entity.getType()); + if (!listeners.isEmpty()) { + notifyListeners(entity, wasOwner, isOwner, hasOwner, + listeners.stream().map(listenerActorMap::get).collect(Collectors.toList())); + } + } finally { + listenerLock.readLock().unlock(); + } } - private void notifyListeners(DOMEntity entity, String mapKey, boolean wasOwner, boolean isOwner, boolean hasOwner) { - Collection listeners = entityTypeListenerMap.get(mapKey); - if (!listeners.isEmpty()) { - notifyListeners(entity, wasOwner, isOwner, hasOwner, listeners); + void notifyEntityOwnershipListener(final DOMEntity entity, final boolean wasOwner, final boolean isOwner, + final boolean hasOwner, final DOMEntityOwnershipListener listener) { + listenerLock.readLock().lock(); + try { + notifyListeners(entity, wasOwner, isOwner, hasOwner, ImmutableList.of(listenerActorMap.get(listener))); + } finally { + listenerLock.readLock().unlock(); } } - private void notifyListeners(DOMEntity entity, boolean wasOwner, boolean isOwner, boolean hasOwner, - Collection listeners) { + @Holding("listenerLock") + private void notifyListeners(final DOMEntity entity, final boolean wasOwner, final boolean isOwner, + final boolean hasOwner, final Collection listenerEntries) { DOMEntityOwnershipChange changed = new DOMEntityOwnershipChange(entity, EntityOwnershipChangeState.from(wasOwner, isOwner, hasOwner), inJeopardy); - for (DOMEntityOwnershipListener listener: listeners) { - ActorRef listenerActor = listenerActorFor(listener); + for (ListenerActorRefEntry entry: listenerEntries) { + ActorRef listenerActor = entry.actorFor(); LOG.debug("{}: Notifying EntityOwnershipListenerActor {} with {}", logId, listenerActor, changed); @@ -114,44 +154,20 @@ class EntityOwnershipListenerSupport { } } - private void addListener(DOMEntityOwnershipListener listener, String mapKey) { - if (entityTypeListenerMap.put(mapKey, listener)) { - ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener); - if (listenerEntry == null) { - listenerActorMap.put(listener, new ListenerActorRefEntry()); - } else { - listenerEntry.referenceCount++; - } - } - } - - private void removeListener(DOMEntityOwnershipListener listener, String mapKey) { - if (entityTypeListenerMap.remove(mapKey, listener)) { - ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener); + private class ListenerActorRefEntry { + final DOMEntityOwnershipListener listener; - LOG.debug("{}: Found {}", logId, listenerEntry); + @GuardedBy("listenerLock") + ActorRef actorRef; - listenerEntry.referenceCount--; - if (listenerEntry.referenceCount <= 0) { - listenerActorMap.remove(listener); + @GuardedBy("listenerLock") + int referenceCount = 1; - if (listenerEntry.actorRef != null) { - LOG.debug("Killing EntityOwnershipListenerActor {}", listenerEntry.actorRef); - listenerEntry.actorRef.tell(PoisonPill.getInstance(), ActorRef.noSender()); - } - } + ListenerActorRefEntry(final DOMEntityOwnershipListener listener) { + this.listener = listener; } - } - - private ActorRef listenerActorFor(DOMEntityOwnershipListener listener) { - return listenerActorMap.get(listener).actorFor(listener); - } - - private class ListenerActorRefEntry { - ActorRef actorRef; - int referenceCount = 1; - ActorRef actorFor(DOMEntityOwnershipListener listener) { + ActorRef actorFor() { if (actorRef == null) { actorRef = actorContext.actorOf(EntityOwnershipListenerActor.props(listener));