Copy FlowOperationsProcessor defensively for thread safety

Change-Id: Ic5c920b0efc40d472d454b0e1a0305f16b39e98c
diff --git a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
index 50d74fc..b386df4 100644
--- a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
@@ -603,12 +603,20 @@
 
         // Mutable
         private final List<Set<FlowRuleOperation>> stages;
-        private final Set<DeviceId> pendingDevices = new HashSet<>();
+        private final Set<DeviceId> pendingDevices;
         private boolean hasFailed = false;
 
         FlowOperationsProcessor(FlowRuleOperations ops) {
             this.stages = Lists.newArrayList(ops.stages());
             this.fops = ops;
+            this.pendingDevices = new HashSet<>();
+        }
+
+        FlowOperationsProcessor(FlowOperationsProcessor src) {
+            this.fops = src.fops;
+            this.stages = Lists.newArrayList(src.stages);
+            this.pendingDevices = new HashSet<>(src.pendingDevices);
+            this.hasFailed = src.hasFailed;
         }
 
         @Override
@@ -641,7 +649,7 @@
         synchronized void satisfy(DeviceId devId) {
             pendingDevices.remove(devId);
             if (pendingDevices.isEmpty()) {
-                operationsService.execute(this);
+                operationsService.execute(new FlowOperationsProcessor(this));
             }
         }
 
@@ -649,7 +657,7 @@
             hasFailed = true;
             pendingDevices.remove(devId);
             if (pendingDevices.isEmpty()) {
-                operationsService.execute(this);
+                operationsService.execute(new FlowOperationsProcessor(this));
             }
 
             FlowRuleOperations.Builder failedOpsBuilder = FlowRuleOperations.builder();