Ensure host events are handled outside the event loop to avoid interrupts

Change-Id: I545de9a877ca9d6d10264c4af5c199ceb69c9f3b
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
index 2ec54af..26643fc0 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
@@ -99,16 +99,19 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_CircuitID;
 import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_END;
 import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
 import static org.onlab.packet.MacAddress.valueOf;
 import static org.onlab.packet.dhcp.DhcpRelayAgentOption.RelayAgentInfoOptions.CIRCUIT_ID;
+import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
 import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
 
@@ -179,6 +182,9 @@
     private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
     private Dhcp4HandlerUtil dhcp4HandlerUtil = new Dhcp4HandlerUtil();
 
+    private Executor hostEventExecutor = newSingleThreadExecutor(
+        groupedThreads("dhcp4-event-host", "%d", log));
+
     @Activate
     protected void activate() {
         appId = coreService.registerApplication(DHCP_V4_RELAY_APP);
@@ -1487,10 +1493,12 @@
             switch (event.type()) {
                 case HOST_ADDED:
                 case HOST_UPDATED:
-                    hostUpdated(event.subject());
+                    log.trace("Scheduled host event {}", event);
+                    hostEventExecutor.execute(() -> hostUpdated(event.subject()));
                     break;
                 case HOST_REMOVED:
-                    hostRemoved(event.subject());
+                    log.trace("Scheduled host event {}", event);
+                    hostEventExecutor.execute(() -> hostRemoved(event.subject()));
                     break;
                 default:
                     break;
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
index 6c5e8a9..43b78a6 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
@@ -113,9 +113,12 @@
 import java.util.Set;
 import java.util.ArrayList;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
 import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
 import java.util.concurrent.Semaphore;
@@ -199,6 +202,10 @@
     private Dhcp6HandlerUtil dhcp6HandlerUtil = new Dhcp6HandlerUtil();
     private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
     private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
+
+    private Executor hostEventExecutor = newSingleThreadExecutor(
+        groupedThreads("dhcp6-event-host", "%d", log));
+
     private class IpAddressInfo {
         Ip6Address ip6Address;
         long    prefTime;
@@ -1392,10 +1399,12 @@
             switch (event.type()) {
                 case HOST_ADDED:
                 case HOST_UPDATED:
-                    hostUpdated(event.subject());
+                    log.trace("Scheduled host event {}", event);
+                    hostEventExecutor.execute(() -> hostUpdated(event.subject()));
                     break;
                 case HOST_REMOVED:
-                    hostRemoved(event.subject());
+                    log.trace("Scheduled host event {}", event);
+                    hostEventExecutor.execute(() -> hostRemoved(event.subject()));
                     break;
                 default:
                     break;
diff --git a/apps/route-service/app/src/main/java/org/onosproject/routeservice/impl/RouteManager.java b/apps/route-service/app/src/main/java/org/onosproject/routeservice/impl/RouteManager.java
index eb73f69..084c71e 100644
--- a/apps/route-service/app/src/main/java/org/onosproject/routeservice/impl/RouteManager.java
+++ b/apps/route-service/app/src/main/java/org/onosproject/routeservice/impl/RouteManager.java
@@ -55,6 +55,7 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadFactory;
@@ -96,6 +97,9 @@
 
     private ThreadFactory threadFactory;
 
+    protected Executor hostEventExecutor = newSingleThreadExecutor(
+        groupedThreads("rm-event-host", "%d", log));
+
     @Activate
     protected void activate() {
         routeMonitor = new RouteMonitor(this, clusterService, storageService);
@@ -382,10 +386,12 @@
             switch (event.type()) {
             case HOST_ADDED:
             case HOST_UPDATED:
-                hostUpdated(event.subject());
+                log.trace("Scheduled host event {}", event);
+                hostEventExecutor.execute(() -> hostUpdated(event.subject()));
                 break;
             case HOST_REMOVED:
-                hostRemoved(event.subject());
+                log.trace("Scheduled host event {}", event);
+                hostEventExecutor.execute(() -> hostRemoved(event.subject()));
                 break;
             case HOST_MOVED:
                 break;
diff --git a/apps/route-service/app/src/test/java/org/onosproject/routeservice/impl/RouteManagerTest.java b/apps/route-service/app/src/test/java/org/onosproject/routeservice/impl/RouteManagerTest.java
index c6c0f82..8457ac4 100644
--- a/apps/route-service/app/src/test/java/org/onosproject/routeservice/impl/RouteManagerTest.java
+++ b/apps/route-service/app/src/test/java/org/onosproject/routeservice/impl/RouteManagerTest.java
@@ -19,6 +19,7 @@
 import java.util.Collection;
 import java.util.Collections;
 
+import com.google.common.util.concurrent.MoreExecutors;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.Ip4Address;
@@ -103,6 +104,7 @@
 
         routeManager = new TestRouteManager();
         routeManager.hostService = hostService;
+        routeManager.hostEventExecutor = MoreExecutors.directExecutor();
 
         routeManager.clusterService = createNiceMock(ClusterService.class);
         replay(routeManager.clusterService);