From: Robert Varga Date: Mon, 2 Jun 2014 11:58:22 +0000 (+0200) Subject: BUG-1120: introduce generations to ListenerMap X-Git-Tag: release/helium~720^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=1c337a8918c2da99b71002e1e80d760954282ee7 BUG-1120: introduce generations to ListenerMap This removes the synchronized block in the fast path, biasing the implementation heavily towards fast readers. The readers need only to take a volatile reference (AtomicReference), at which point they have an isolated read-only view of the map. Writers on the other hand, though, are fully synchronized and are required to perform a full copy operation, then do the modifications and finally reinstate the read-only view through setting the atomic reference. Change-Id: I5d118177c1508199b66b9e26499c9fc628d3f65e Signed-off-by: Robert Varga --- diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/GenerationalListenerMap.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/GenerationalListenerMap.java index 05ccd9fa78..85265e8fd4 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/GenerationalListenerMap.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/GenerationalListenerMap.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, @@ -7,62 +7,54 @@ */ package org.opendaylight.controller.sal.binding.impl; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import javax.annotation.concurrent.GuardedBy; import org.opendaylight.yangtools.yang.binding.Notification; -import com.google.common.base.Predicate; import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; +/** + * A multi-generation, isolated notification type to listener map. + */ final class GenerationalListenerMap { - private final Multimap, NotificationListenerRegistration> listeners = - HashMultimap.create(); + private final AtomicReference current = new AtomicReference<>(new ListenerMapGeneration()); - private static Iterable> getNotificationTypes(final Notification notification) { - final Class[] ifaces = notification.getClass().getInterfaces(); - return Iterables.filter(Arrays.asList(ifaces), new Predicate>() { - @Override - public boolean apply(final Class input) { - if (Notification.class.equals(input)) { - return false; - } - return Notification.class.isAssignableFrom(input); - } - }); + Iterable> listenersFor(final Notification notification) { + return current.get().listenersFor(notification); } - synchronized Iterable> listenersFor(final Notification notification) { - final Set> toNotify = new HashSet<>(); - - for (final Class type : getNotificationTypes(notification)) { - final Collection> l = listeners.get((Class) type); - if (l != null) { - toNotify.addAll(l); - } - } - - return toNotify; + Iterable> getKnownTypes() { + // Note: this relies on current having immutable listeners + return current.get().getListeners().keySet(); } - synchronized Iterable> getKnownTypes() { - return ImmutableList.copyOf(listeners.keySet()); + @GuardedBy("this") + private Multimap, NotificationListenerRegistration> mutableListeners() { + return HashMultimap.create(current.get().getListeners()); } synchronized void addRegistrations(final NotificationListenerRegistration... registrations) { + Multimap, NotificationListenerRegistration> listeners = + mutableListeners(); + for (NotificationListenerRegistration reg : registrations) { listeners.put(reg.getType(), reg); } + + current.set(new ListenerMapGeneration(listeners)); } synchronized void removeRegistrations(final NotificationListenerRegistration... registrations) { + Multimap, NotificationListenerRegistration> listeners = + mutableListeners(); + for (NotificationListenerRegistration reg : registrations) { listeners.remove(reg.getType(), reg); } + + current.set(new ListenerMapGeneration(listeners)); } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/ListenerMapGeneration.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/ListenerMapGeneration.java new file mode 100644 index 0000000000..89e3feeb6f --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/ListenerMapGeneration.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.binding.impl; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.opendaylight.yangtools.yang.binding.Notification; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; + +/** + * An immutable view of the current generation of listeners. + */ +final class ListenerMapGeneration { + private final Multimap, NotificationListenerRegistration> listeners; + + ListenerMapGeneration() { + listeners = ImmutableMultimap.of(); + } + + ListenerMapGeneration(final Multimap, NotificationListenerRegistration> listeners) { + this.listeners = ImmutableMultimap.copyOf(listeners); + } + + /** + * Current listeners. Exposed for creating the next generation. + * + * @return Current type-to-listener map. + */ + Multimap, NotificationListenerRegistration> getListeners() { + return listeners; + } + + private static Iterable> getNotificationTypes(final Notification notification) { + final Class[] ifaces = notification.getClass().getInterfaces(); + return Iterables.filter(Arrays.asList(ifaces), new Predicate>() { + @Override + public boolean apply(final Class input) { + if (Notification.class.equals(input)) { + return false; + } + return Notification.class.isAssignableFrom(input); + } + }); + } + + /** + * Look up the listeners which need to see this notification delivered. + * + * @param notification Notification object + * @return Iterable of listeners, may be null + * + * FIXME: improve such that it always returns non-null. + */ + public Iterable> listenersFor(final Notification notification) { + final Set> ret = new HashSet<>(); + + for (final Class type : getNotificationTypes(notification)) { + final Collection> l = listeners.get((Class) type); + if (l != null) { + ret.addAll(l); + } + } + + return ret; + } +} \ No newline at end of file